mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +00:00 
			
		
		
		
	Release version 0.1.20
This commit is contained in:
		| @@ -80,6 +80,7 @@ def certidude_app(): | ||||
|     from .whois import WhoisResource | ||||
|     from .log import LogResource | ||||
|     from .tag import TagResource, TagDetailResource | ||||
|     from .cfg import ConfigResource, ScriptResource | ||||
|  | ||||
|     app = falcon.API() | ||||
|  | ||||
| @@ -94,6 +95,8 @@ def certidude_app(): | ||||
|     app.add_route("/api/log/", LogResource()) | ||||
|     app.add_route("/api/tag/", TagResource()) | ||||
|     app.add_route("/api/tag/{identifier}/", TagDetailResource()) | ||||
|     app.add_route("/api/config/", ConfigResource()) | ||||
|     app.add_route("/api/script/", ScriptResource()) | ||||
|     app.add_route("/api/", SessionResource()) | ||||
|  | ||||
|     # Gateway API calls, should this be moved to separate project? | ||||
| @@ -115,7 +118,6 @@ def certidude_app(): | ||||
|     class PushLogHandler(logging.Handler): | ||||
|         def emit(self, record): | ||||
|             from certidude.push import publish | ||||
|             print("EVENT HAPPENED:", record.created) | ||||
|             publish("log-entry", dict( | ||||
|                 created = datetime.fromtimestamp(record.created), | ||||
|                 message = record.msg % record.args, | ||||
|   | ||||
							
								
								
									
										113
									
								
								certidude/api/cfg.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								certidude/api/cfg.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| import falcon | ||||
| import logging | ||||
| import ipaddress | ||||
| import string | ||||
| from random import choice | ||||
| from certidude import config | ||||
| from certidude.auth import login_required, authorize_admin | ||||
| from certidude.decorators import serialize | ||||
| from jinja2 import Environment, FileSystemLoader | ||||
|  | ||||
| logger = logging.getLogger("api") | ||||
|  | ||||
| env = Environment(loader=FileSystemLoader("/etc/certidude/scripts"), trim_blocks=True) | ||||
|  | ||||
| SQL_SELECT_INHERITED = """ | ||||
| select `key`, `value` from tag_inheritance where tag_id in (select | ||||
| 	tag.id | ||||
| from | ||||
| 	device_tag | ||||
| join | ||||
| 	tag on device_tag.tag_id = tag.id | ||||
| join | ||||
| 	device on device_tag.device_id = device.id | ||||
| where | ||||
| 	device.cn = %s) | ||||
| """ | ||||
|  | ||||
| SQL_SELECT_TAGS = """ | ||||
| select | ||||
| 	tag.`key` as `key`, | ||||
| 	tag.`value` as `value` | ||||
| from | ||||
| 	device_tag | ||||
| join | ||||
| 	tag on device_tag.tag_id = tag.id | ||||
| join | ||||
| 	device on device_tag.device_id = device.id | ||||
| where | ||||
|     device.cn = %s | ||||
| """ | ||||
|  | ||||
| SQL_SELECT_INHERITANCE = """ | ||||
| select | ||||
|     tag_inheritance.`id` as `id`, | ||||
|     tag.id as `tag_id`, | ||||
|     tag.`key` as `match_key`, | ||||
|     tag.`value` as `match_value`, | ||||
|     tag_inheritance.`key` as `key`, | ||||
|     tag_inheritance.`value` as `value` | ||||
| from tag_inheritance | ||||
| join tag on tag.id = tag_inheritance.tag_id | ||||
| """ | ||||
|  | ||||
| class ConfigResource(object): | ||||
|     @serialize | ||||
|     @login_required | ||||
|     @authorize_admin | ||||
|     def on_get(self, req, resp): | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor(dictionary=True) | ||||
|         cursor.execute(SQL_SELECT_INHERITANCE) | ||||
|         def g(): | ||||
|             for row in cursor: | ||||
|                 yield row | ||||
|             cursor.close() | ||||
|             conn.close() | ||||
|         return g() | ||||
|  | ||||
| class ScriptResource(object): | ||||
|     def on_get(self, req, resp): | ||||
|         from certidude.api.whois import address_to_identity | ||||
|  | ||||
|         node = address_to_identity( | ||||
|             config.DATABASE_POOL.get_connection(), | ||||
|             ipaddress.ip_address(req.env["REMOTE_ADDR"]) | ||||
|         ) | ||||
|         if not node: | ||||
|             resp.body = "Could not map IP address: %s" % req.env["REMOTE_ADDR"] | ||||
|             resp.status = falcon.HTTP_404 | ||||
|             return | ||||
|  | ||||
|         address, acquired, identity = node | ||||
|  | ||||
|         key, common_name = identity.split("=") | ||||
|         assert "=" not in common_name | ||||
|  | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor() | ||||
|  | ||||
|         resp.set_header("Content-Type", "text/x-shellscript") | ||||
|  | ||||
|         args = common_name, | ||||
|         ctx = dict() | ||||
|  | ||||
|         for query in SQL_SELECT_INHERITED, SQL_SELECT_TAGS: | ||||
|             cursor.execute(query, args) | ||||
|  | ||||
|             for key, value in cursor: | ||||
|                 current = ctx | ||||
|                 if "." in key: | ||||
|                     path, key = key.rsplit(".", 1) | ||||
|  | ||||
|                     for component in path.split("."): | ||||
|                         if component not in current: | ||||
|                             current[component] = dict() | ||||
|                         current = current[component] | ||||
|                 current[key] = value | ||||
|         cursor.close() | ||||
|         conn.close() | ||||
|  | ||||
|         resp.body = env.get_template("uci.sh").render(ctx) | ||||
|  | ||||
|         # TODO: Assert time is within reasonable range | ||||
| @@ -54,8 +54,8 @@ class LeaseResource(object): | ||||
|                 addresses.id | ||||
|             desc | ||||
|         """ | ||||
|         cnx = config.DATABASE_POOL.get_connection() | ||||
|         cursor = cnx.cursor() | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor() | ||||
|         cursor.execute(SQL_LEASES) | ||||
|  | ||||
|         for acquired, released, address, identity in cursor: | ||||
| @@ -66,3 +66,5 @@ class LeaseResource(object): | ||||
|                 "identity": parse_dn(bytes(identity)) | ||||
|             } | ||||
|  | ||||
|         cursor.close() | ||||
|         conn.close() | ||||
|   | ||||
| @@ -28,6 +28,9 @@ class SignedCertificateListResource(object): | ||||
| class SignedCertificateDetailResource(object): | ||||
|     @serialize | ||||
|     def on_get(self, req, resp, cn): | ||||
|         # Compensate for NTP lag | ||||
|         from time import sleep | ||||
|         sleep(5) | ||||
|         try: | ||||
|             logger.info("Served certificate %s to %s", cn, req.env["REMOTE_ADDR"]) | ||||
|             resp.set_header("Content-Disposition", "attachment; filename=%s.crt" % cn) | ||||
|   | ||||
| @@ -7,6 +7,26 @@ from certidude.decorators import serialize | ||||
|  | ||||
| logger = logging.getLogger("api") | ||||
|  | ||||
| SQL_TAG_LIST = """ | ||||
| select | ||||
|     device_tag.id as `id`, | ||||
| 	tag.key as `key`, | ||||
| 	tag.value as `value`, | ||||
| 	device.cn as `cn` | ||||
| from | ||||
| 	device_tag | ||||
| join | ||||
| 	tag | ||||
| on | ||||
| 	device_tag.tag_id = tag.id | ||||
| join | ||||
| 	device | ||||
| on | ||||
| 	device_tag.device_id = device.id | ||||
| """ | ||||
|  | ||||
| SQL_TAG_DETAIL = SQL_TAG_LIST + " where device_tag.id = %s" | ||||
|  | ||||
| class TagResource(object): | ||||
|     @serialize | ||||
|     @login_required | ||||
| @@ -14,7 +34,7 @@ class TagResource(object): | ||||
|     def on_get(self, req, resp): | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor(dictionary=True) | ||||
|         cursor.execute("select * from tag") | ||||
|         cursor.execute(SQL_TAG_LIST) | ||||
|  | ||||
|         def g(): | ||||
|             for row in cursor: | ||||
| @@ -30,10 +50,24 @@ class TagResource(object): | ||||
|         from certidude import push | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor() | ||||
|         args = req.get_param("cn"), req.get_param("key"), req.get_param("value") | ||||
|  | ||||
|         args = req.get_param("cn"), | ||||
|         cursor.execute( | ||||
|             "insert into tag (`cn`, `key`, `value`) values (%s, %s, %s)", args) | ||||
|             "insert ignore device (`cn`) values (%s) on duplicate key update used = NOW();", args) | ||||
|         device_id = cursor.lastrowid | ||||
|  | ||||
|         args = req.get_param("key"), req.get_param("value") | ||||
|         cursor.execute( | ||||
|             "insert into tag (`key`, `value`) values (%s, %s) on duplicate key update used = NOW();", args) | ||||
|         tag_id = cursor.lastrowid | ||||
|  | ||||
|         args = device_id, tag_id | ||||
|         cursor.execute( | ||||
|             "insert into device_tag (`device_id`, `tag_id`) values (%s, %s);", args) | ||||
|  | ||||
|         push.publish("tag-added", str(cursor.lastrowid)) | ||||
|  | ||||
|         args = req.get_param("cn"), req.get_param("key"), req.get_param("value") | ||||
|         logger.debug("Tag cn=%s, key=%s, value=%s added" % args) | ||||
|         conn.commit() | ||||
|         cursor.close() | ||||
| @@ -47,7 +81,7 @@ class TagDetailResource(object): | ||||
|     def on_get(self, req, resp, identifier): | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor(dictionary=True) | ||||
|         cursor.execute("select * from tag where `id` = %s", (identifier,)) | ||||
|         cursor.execute(SQL_TAG_DETAIL, (identifier,)) | ||||
|         for row in cursor: | ||||
|             cursor.close() | ||||
|             conn.close() | ||||
| @@ -63,11 +97,21 @@ class TagDetailResource(object): | ||||
|         from certidude import push | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor() | ||||
|         cursor.execute("update tag set `value` = %s where `id` = %s limit 1", | ||||
|             (req.get_param("value"), identifier)) | ||||
|  | ||||
|         # Create tag if necessary | ||||
|         args = req.get_param("key"), req.get_param("value") | ||||
|         cursor.execute( | ||||
|             "insert into tag (`key`, `value`) values (%s, %s) on duplicate key update used = NOW();", args) | ||||
|         tag_id = cursor.lastrowid | ||||
|  | ||||
|         # Attach tag to device | ||||
|         cursor.execute("update device_tag set tag_id = %s where `id` = %s limit 1", | ||||
|             (tag_id, identifier)) | ||||
|         conn.commit() | ||||
|  | ||||
|         cursor.close() | ||||
|         conn.close() | ||||
|  | ||||
|         logger.debug("Tag %s updated, value set to %s", | ||||
|             identifier, req.get_param("value")) | ||||
|         push.publish("tag-updated", identifier) | ||||
| @@ -80,7 +124,7 @@ class TagDetailResource(object): | ||||
|         from certidude import push | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|         cursor = conn.cursor() | ||||
|         cursor.execute("delete from tag where tag.id = %s", (identifier,)) | ||||
|         cursor.execute("delete from device_tag where id = %s", (identifier,)) | ||||
|         conn.commit() | ||||
|         cursor.close() | ||||
|         conn.close() | ||||
|   | ||||
| @@ -6,7 +6,7 @@ from certidude import config | ||||
| from certidude.decorators import serialize | ||||
| from certidude.api.lease import parse_dn | ||||
|  | ||||
| def address_to_identity(cnx, addr): | ||||
| def address_to_identity(conn, addr): | ||||
|     """ | ||||
|     Translate currently online client's IP-address to distinguished name | ||||
|     """ | ||||
| @@ -27,29 +27,32 @@ def address_to_identity(cnx, addr): | ||||
|             released is not null | ||||
|     """ | ||||
|  | ||||
|     cursor = cnx.cursor() | ||||
|     cursor = conn.cursor() | ||||
|     import struct | ||||
|     cursor.execute(SQL_LEASES, (struct.pack("!L", int(addr)),)) | ||||
|  | ||||
|     for acquired, released, identity in cursor: | ||||
|         return { | ||||
|             "address": addr, | ||||
|             "acquired": datetime.utcfromtimestamp(acquired), | ||||
|             "identity": parse_dn(bytes(identity)) | ||||
|         } | ||||
|         cursor.close() | ||||
|         return addr, datetime.utcfromtimestamp(acquired), parse_dn(bytes(identity)) | ||||
|  | ||||
|     cursor.close() | ||||
|     return None | ||||
|  | ||||
|  | ||||
| class WhoisResource(object): | ||||
|     @serialize | ||||
|     def on_get(self, req, resp): | ||||
|         conn = config.DATABASE_POOL.get_connection() | ||||
|  | ||||
|         identity = address_to_identity( | ||||
|             config.DATABASE_POOL.get_connection(), | ||||
|             conn, | ||||
|             ipaddress.ip_address(req.get_param("address") or req.env["REMOTE_ADDR"]) | ||||
|         ) | ||||
|  | ||||
|         conn.close() | ||||
|  | ||||
|         if identity: | ||||
|             return identity | ||||
|             return dict(address=identity[0], acquired=identity[1], identity=identity[2]) | ||||
|         else: | ||||
|             resp.status = falcon.HTTP_403 | ||||
|             resp.body = "Failed to look up node %s" % req.env["REMOTE_ADDR"] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user