mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Use filesystem extended attribute user.xdg.tags for tags, move leases to user.lease namespace
This commit is contained in:
		| @@ -59,15 +59,29 @@ class SessionResource(object): | ||||
|  | ||||
|         def serialize_certificates(g): | ||||
|             for common_name, path, buf, obj, server in g(): | ||||
|                 # Extract certificate tags from filesystem | ||||
|                 try: | ||||
|                     last_seen = datetime.strptime(xattr.getxattr(path, "user.last_seen"), "%Y-%m-%dT%H:%M:%S.%fZ") | ||||
|                     tags = [] | ||||
|                     for tag in xattr.getxattr(path, "user.xdg.tags").split(","): | ||||
|                         if "=" in tag: | ||||
|                             k, v = tag.split("=", 1) | ||||
|                         else: | ||||
|                             k, v = "other", tag | ||||
|                         tags.append(dict(id=tag, key=k, value=v)) | ||||
|                 except IOError: # No such attribute(s) | ||||
|                     tags = None | ||||
|  | ||||
|                 # Extract lease information from filesystem | ||||
|                 try: | ||||
|                     last_seen = datetime.strptime(xattr.getxattr(path, "user.lease.last_seen"), "%Y-%m-%dT%H:%M:%S.%fZ") | ||||
|                     lease = dict( | ||||
|                         address = xattr.getxattr(path, "user.address"), | ||||
|                         address = xattr.getxattr(path, "user.lease.address"), | ||||
|                         last_seen = last_seen, | ||||
|                         age = datetime.utcnow() - last_seen | ||||
|                     ) | ||||
|                 except IOError: # No such attribute(s) | ||||
|                     lease = None | ||||
|  | ||||
|                 yield dict( | ||||
|                     serial_number = "%x" % obj.serial_number, | ||||
|                     common_name = common_name, | ||||
| @@ -77,10 +91,7 @@ class SessionResource(object): | ||||
|                     expires = obj.not_valid_after, | ||||
|                     sha256sum = hashlib.sha256(buf).hexdigest(), | ||||
|                     lease = lease, | ||||
|                     tags = dict([ | ||||
|                         (j[9:], xattr.getxattr(path, j).decode("utf-8")) | ||||
|                         for j in xattr.listxattr(path) | ||||
|                         if j.startswith("user.tag.")]) | ||||
|                     tags = tags | ||||
|                 ) | ||||
|  | ||||
|         if req.context.get("user").is_admin(): | ||||
| @@ -96,6 +107,7 @@ class SessionResource(object): | ||||
|             ), | ||||
|             request_submission_allowed = config.REQUEST_SUBMISSION_ALLOWED, | ||||
|             authority = dict( | ||||
|                 tagging = [dict(name=t[0], type=t[1], title=t[2]) for t in config.TAG_TYPES], | ||||
|                 lease = dict( | ||||
|                     offline = 600, # Seconds from last seen activity to consider lease offline, OpenVPN reneg-sec option | ||||
|                     dead = 604800 # Seconds from last activity to consider lease dead, X509 chain broken or machine discarded | ||||
| @@ -199,7 +211,7 @@ def certidude_app(): | ||||
|     app.add_route("/api/signed/{cn}/lease/", LeaseDetailResource()) | ||||
|  | ||||
|     # API call used to delete existing tags | ||||
|     app.add_route("/api/signed/{cn}/tag/{key}/", TagDetailResource()) | ||||
|     app.add_route("/api/signed/{cn}/tag/{tag}/", TagDetailResource()) | ||||
|  | ||||
|     # Gateways can submit leases via this API call | ||||
|     app.add_route("/api/lease/", LeaseResource()) | ||||
|   | ||||
| @@ -16,8 +16,8 @@ class LeaseDetailResource(object): | ||||
|     def on_get(self, req, resp, cn): | ||||
|         path, buf, cert = authority.get_signed(cn) | ||||
|         return dict( | ||||
|             last_seen = xattr.getxattr(path, "user.last_seen"), | ||||
|             address = xattr.getxattr(path, "user.address").decode("ascii") | ||||
|             last_seen = xattr.getxattr(path, "user.lease.last_seen"), | ||||
|             address = xattr.getxattr(path, "user.lease.address").decode("ascii") | ||||
|         ) | ||||
|  | ||||
|  | ||||
| @@ -29,8 +29,8 @@ class LeaseResource(object): | ||||
|         if cert.serial != req.get_param_as_int("serial", required=True): # Badum we have OCSP! | ||||
|             raise # TODO proper exception | ||||
|         if req.get_param("action") == "client-connect": | ||||
|             xattr.setxattr(path, "user.address", req.get_param("address", required=True).encode("ascii")) | ||||
|             xattr.setxattr(path, "user.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z") | ||||
|             xattr.setxattr(path, "user.lease.address", req.get_param("address", required=True).encode("ascii")) | ||||
|             xattr.setxattr(path, "user.lease.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z") | ||||
|             push.publish("lease-update", common_name) | ||||
|  | ||||
|         # client-disconnect is pretty much unusable: | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import falcon | ||||
| import logging | ||||
| import xattr | ||||
| from certidude import authority | ||||
| from xattr import getxattr, removexattr, setxattr | ||||
| from certidude import authority, push | ||||
| from certidude.auth import login_required, authorize_admin | ||||
| from certidude.decorators import serialize, csrf_protection | ||||
|  | ||||
| @@ -13,19 +13,34 @@ class TagResource(object): | ||||
|     @authorize_admin | ||||
|     def on_get(self, req, resp, cn): | ||||
|         path, buf, cert = authority.get_signed(cn) | ||||
|         return dict([ | ||||
|             (k[9:], xattr.getxattr(path, k)) | ||||
|             for k in xattr.listxattr(path) | ||||
|             if k.startswith("user.tag.")]) | ||||
|         tags = [] | ||||
|         try: | ||||
|             for tag in getxattr(path, "user.xdg.tags").split(","): | ||||
|                 if "=" in tag: | ||||
|                     k, v = tag.split("=", 1) | ||||
|                 else: | ||||
|                     k, v = "other", tag | ||||
|                 tags.append(dict(id=tag, key=k, value=v)) | ||||
|         except IOError: # No user.xdg.tags attribute | ||||
|             pass | ||||
|         return tags | ||||
|  | ||||
|  | ||||
|     @csrf_protection | ||||
|     @login_required | ||||
|     @authorize_admin | ||||
|     def on_post(self, req, resp, cn): | ||||
|         from certidude import push | ||||
|         path, buf, cert = authority.get_signed(cn) | ||||
|         key, value = req.get_param("key", required=True), req.get_param("value", required=True) | ||||
|         xattr.setxattr(path, "user.tag.%s" % key, value.encode("utf-8")) | ||||
|         try: | ||||
|             tags = set(getxattr(path, "user.xdg.tags").decode("utf-8").split(",")) | ||||
|         except IOError: | ||||
|             tags = set() | ||||
|         if key == "other": | ||||
|             tags.add(value) | ||||
|         else: | ||||
|             tags.add("%s=%s" % (key,value)) | ||||
|         setxattr(path, "user.xdg.tags", ",".join(tags).encode("utf-8")) | ||||
|         logger.debug(u"Tag %s=%s set for %s" % (key, value, cn)) | ||||
|         push.publish("tag-update", cn) | ||||
|  | ||||
| @@ -34,9 +49,32 @@ class TagDetailResource(object): | ||||
|     @csrf_protection | ||||
|     @login_required | ||||
|     @authorize_admin | ||||
|     def on_delete(self, req, resp, cn, key): | ||||
|         from certidude import push | ||||
|     def on_put(self, req, resp, cn, tag): | ||||
|         path, buf, cert = authority.get_signed(cn) | ||||
|         xattr.removexattr(path, "user.tag.%s" % key) | ||||
|         logger.debug(u"Tag %s removed for %s" % (key, cn)) | ||||
|         value = req.get_param("value", required=True) | ||||
|         try: | ||||
|             tags = set(getxattr(path, "user.xdg.tags").decode("utf-8").split(",")) | ||||
|         except IOError: | ||||
|             tags = set() | ||||
|         tags.remove(tag) | ||||
|         if "=" in tag: | ||||
|             tags.add("%s=%s" % (tag.split("=")[0], value)) | ||||
|         else: | ||||
|             tags.add(value) | ||||
|         setxattr(path, "user.xdg.tags", ",".join(tags).encode("utf-8")) | ||||
|         logger.debug(u"Tag %s set to %s for %s" % (tag, value, cn)) | ||||
|         push.publish("tag-update", cn) | ||||
|  | ||||
|     @csrf_protection | ||||
|     @login_required | ||||
|     @authorize_admin | ||||
|     def on_delete(self, req, resp, cn, tag): | ||||
|         path, buf, cert = authority.get_signed(cn) | ||||
|         tags = set(getxattr(path, "user.xdg.tags").split(",")) | ||||
|         tags.remove(tag) | ||||
|         if not tags: | ||||
|             removexattr(path, "user.xdg.tags") | ||||
|         else: | ||||
|             setxattr(path, "user.xdg.tags", ",".join(tags)) | ||||
|         logger.debug(u"Tag %s removed for %s" % (tag, cn)) | ||||
|         push.publish("tag-update", cn) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user