mirror of
https://github.com/laurivosandi/certidude
synced 2026-01-12 17:06:59 +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