json-serialize-output
blallo 2020-11-20 12:04:17 +01:00
parent 2f2142025f
commit 29685a66b5
Signed by: blallo
GPG Key ID: 0CBE577C9B72DC3F
11 changed files with 134 additions and 117 deletions

View File

@ -8,11 +8,11 @@ log = get_logger(__name__)
def api_startup(app): def api_startup(app):
app['ldap_client'].open() app["ldap_client"].open()
def api_shutdown(app): def api_shutdown(app):
app['ldap_client'].close() app["ldap_client"].close()
def api_app(config): def api_app(config):
@ -20,8 +20,8 @@ def api_app(config):
app = web.Application() app = web.Application()
ldap_client = Client(**config.get('ldap', {})) ldap_client = Client(**config.get("ldap", {}))
app['ldap_client'] = ldap_client app["ldap_client"] = ldap_client
app.on_startup.append(api_startup) app.on_startup.append(api_startup)
app.on_shutdown.append(api_shutdown) app.on_shutdown.append(api_shutdown)

View File

@ -10,12 +10,12 @@ log = get_logger(__name__)
class User(View): class User(View):
async def get(self): async def get(self):
uid = self.request.match_info.get('uid', None) uid = self.request.match_info.get("uid", None)
if uid is None: if uid is None:
return HTTPUnprocessableEntity() return HTTPUnprocessableEntity()
client = self.request.app['ldap_client'] client = self.request.app["ldap_client"]
user = get_user_by_uid(client, uid) user = get_user_by_uid(client, uid)
if not user: if not user:

View File

@ -4,7 +4,7 @@ from phi.api.rest import User
api_routes = [ api_routes = [
route('*', '/user', User), route("*", "/user", User),
route('*', '/user/', User), route("*", "/user/", User),
route('*', '/user/{uid}', User) route("*", "/user/{uid}", User),
] ]

View File

@ -2,5 +2,4 @@ from datetime import datetime
def serialize(d): def serialize(d):
return {k: (v.isoformat() if isinstance(v, datetime) else v) return {k: (v.isoformat() if isinstance(v, datetime) else v) for k, v in d.items()}
for k, v in d.items()}

View File

@ -8,15 +8,17 @@ def setup_app(config):
loop = get_event_loop() loop = get_event_loop()
app = web.Application(loop=loop) app = web.Application(loop=loop)
app['config'] = config app["config"] = config
api = api_app(config) api = api_app(config)
app.add_subapp('/api', api) app.add_subapp("/api", api)
return app return app
def run_app(app): def run_app(app):
web.run_app(app, web.run_app(
host=app['config']['core']['listen'].get('host', '127.0.0.1'), app,
port=app['config']['core']['listen'].get('port', '8080')) host=app["config"]["core"]["listen"].get("host", "127.0.0.1"),
port=app["config"]["core"]["listen"].get("port", "8080"),
)

View File

@ -2,15 +2,16 @@ import os.path
import yaml import yaml
NAME = 'phi' NAME = "phi"
CONFIG_FILE = 'config.yml' CONFIG_FILE = "config.yml"
CONFIG_PATHS = ['./', CONFIG_PATHS = [
'~/.config/' + NAME + '/', "./",
'/usr/local/etc/' + NAME + '/', "~/.config/" + NAME + "/",
'/etc/' + NAME + '/'] "/usr/local/etc/" + NAME + "/",
CONFIG_FILES = [os.path.join(p, CONFIG_FILE) "/etc/" + NAME + "/",
for p in CONFIG_PATHS] ]
CONFIG_FILES = [os.path.join(p, CONFIG_FILE) for p in CONFIG_PATHS]
def get_config(custom_config=None): def get_config(custom_config=None):
@ -25,7 +26,7 @@ def get_config(custom_config=None):
for f in CONFIG_FILES: for f in CONFIG_FILES:
try: try:
with open(f, 'r') as c: with open(f, "r") as c:
config = yaml.safe_load(c) config = yaml.safe_load(c)
return (f, config) return (f, config)
except FileNotFoundError: except FileNotFoundError:
@ -36,9 +37,10 @@ def get_config(custom_config=None):
pass pass
else: else:
if custom_config: if custom_config:
raise FileNotFoundError('Config file {} not found.' raise FileNotFoundError("Config file {} not found.".format(custom_config))
.format(custom_config))
else: else:
raise FileNotFoundError("Could not find {} in any of {}." raise FileNotFoundError(
.format(CONFIG_FILE, "Could not find {} in any of {}.".format(
', '.join(CONFIG_PATHS))) CONFIG_FILE, ", ".join(CONFIG_PATHS)
)
)

View File

@ -10,11 +10,18 @@ set_library_log_detail_level(PROTOCOL)
class Client: class Client:
def __init__(self, def __init__(
host=None, port=389, self,
encryption=None, ciphers=None, validate=False, ca_certs=None, host=None,
username=None, password=None, port=389,
base_dn=None): encryption=None,
ciphers=None,
validate=False,
ca_certs=None,
username=None,
password=None,
base_dn=None,
):
log.info("Initializing LDAP Client.") log.info("Initializing LDAP Client.")
self.host = host self.host = host
@ -31,13 +38,16 @@ class Client:
self.base_dn = base_dn self.base_dn = base_dn
self.connection_lock = Lock() self.connection_lock = Lock()
self.connection = make_connection(host=self.host, port=self.port, self.connection = make_connection(
encryption=self.encryption, host=self.host,
ciphers=self.ciphers, port=self.port,
validate=self.validate, encryption=self.encryption,
ca_certs=self.ca_certs, ciphers=self.ciphers,
username=self.username, validate=self.validate,
password=self.password) ca_certs=self.ca_certs,
username=self.username,
password=self.password,
)
def open(self): def open(self):
self.connection_lock.acquire() self.connection_lock.acquire()
@ -46,8 +56,7 @@ class Client:
self.connection_lock.release() self.connection_lock.release()
else: else:
self.connection_lock.release() self.connection_lock.release()
raise Exception("Trying to open a connection, " raise Exception("Trying to open a connection, " "but it is already open.")
"but it is already open.")
def close(self): def close(self):
self.connection_lock.acquire() self.connection_lock.acquire()

View File

@ -6,9 +6,16 @@ from phi.logging import get_logger
log = get_logger(__name__) log = get_logger(__name__)
def make_connection(host=None, port=389, def make_connection(
encryption=None, ciphers=None, validate=False, host=None,
ca_certs=None, username=None, password=None): port=389,
encryption=None,
ciphers=None,
validate=False,
ca_certs=None,
username=None,
password=None,
):
# TLSv1.2 is supported since Python 3.4 # TLSv1.2 is supported since Python 3.4
if encryption is None: if encryption is None:
log.warning("The connection to the LDAP server will not be encrypted.") log.warning("The connection to the LDAP server will not be encrypted.")
@ -20,23 +27,26 @@ def make_connection(host=None, port=389,
raise NotImplementedError("Sorry, use TLSv1.2.") raise NotImplementedError("Sorry, use TLSv1.2.")
if encryption is not None and ciphers is not None: if encryption is not None and ciphers is not None:
log.info("The connection to the LDAP server will use the " log.info(
"following ciphers: {}".format(ciphers)) "The connection to the LDAP server will use the "
"following ciphers: {}".format(ciphers)
)
tls.ciphers = ciphers tls.ciphers = ciphers
if encryption is not None and validate is True: if encryption is not None and validate is True:
log.info("The certificate hostname will be checked to match the " log.info(
"remote hostname.") "The certificate hostname will be checked to match the " "remote hostname."
)
tls.validate = CERT_REQUIRED tls.validate = CERT_REQUIRED
if encryption is not None and validate is True and ca_certs is not None: if encryption is not None and validate is True and ca_certs is not None:
log.info("Using the following CA certificates: {}" log.info("Using the following CA certificates: {}".format(ca_certs))
.format(ca_certs))
tls.ca_certs_file = ca_certs tls.ca_certs_file = ca_certs
server = Server(host=host, port=port, tls=tls) server = Server(host=host, port=port, tls=tls)
connection = Connection(server, user=username, password=password, connection = Connection(
client_strategy=ASYNC) server, user=username, password=password, client_strategy=ASYNC
)
return connection return connection

View File

@ -8,13 +8,14 @@ log = get_logger(__name__)
def get_group_by_cn(client, cn): def get_group_by_cn(client, cn):
log.info("Searching groups with common name: {}".format(cn)) log.info("Searching groups with common name: {}".format(cn))
dn = 'cn={},ou=Groups,{}'.format(cn, client.base_dn) dn = "cn={},ou=Groups,{}".format(cn, client.base_dn)
log.debug("Search dn: {}".format(dn)) log.debug("Search dn: {}".format(dn))
response_id = client.connection.search( response_id = client.connection.search(
dn, '(objectclass=groupOfNames)', dn,
search_scope='SUBTREE', "(objectclass=groupOfNames)",
attributes=[ALL_ATTRIBUTES] search_scope="SUBTREE",
attributes=[ALL_ATTRIBUTES],
) )
response = get_response(client, response_id) response = get_response(client, response_id)
@ -23,9 +24,10 @@ def get_group_by_cn(client, cn):
return None return None
if len(response) > 1: if len(response) > 1:
log.error("Looking for exactly one result but server gave {}. " log.error(
"Taking the first and ignoring the rest." "Looking for exactly one result but server gave {}. "
.format(len(response))) "Taking the first and ignoring the rest.".format(len(response))
)
group = make_group_dict(client, response[0]) group = make_group_dict(client, response[0])
return group return group
@ -33,14 +35,15 @@ def get_group_by_cn(client, cn):
def get_all_groups(client): def get_all_groups(client):
log.info("Searching all the groups") log.info("Searching all the groups")
dn = 'ou=Groups,{}'.format(client.base_dn) dn = "ou=Groups,{}".format(client.base_dn)
log.debug("Search dn: {}".format(dn)) log.debug("Search dn: {}".format(dn))
response_id = client.connection.search( response_id = client.connection.search(
dn, '(objectclass=groupOfNames)', dn,
search_scope='SUBTREE', "(objectclass=groupOfNames)",
attributes=[ALL_ATTRIBUTES] search_scope="SUBTREE",
attributes=[ALL_ATTRIBUTES],
) )
response = get_response(client, response_id) response = get_response(client, response_id)
@ -49,13 +52,12 @@ def get_all_groups(client):
def add_group_member(client, group, user): def add_group_member(client, group, user):
group_dn = group['dn'] group_dn = group["dn"]
member_dn = user['dn'] member_dn = user["dn"]
log.debug('Found adding {} to {}'.format(member_dn, group_dn)) log.debug("Found adding {} to {}".format(member_dn, group_dn))
response_id = client.connection.modify( response_id = client.connection.modify(
group_dn, group_dn, {"member": [(MODIFY_ADD, [member_dn])]}
{'member': [(MODIFY_ADD, [member_dn])]}
) )
return get_response(client, response_id) return get_response(client, response_id)

View File

@ -9,13 +9,11 @@ log = get_logger(__name__)
def get_user_by_uid(client, uid): def get_user_by_uid(client, uid):
log.info("Searching entry with identifier: {}".format(uid)) log.info("Searching entry with identifier: {}".format(uid))
filter_ = "({}={})".format('uid', uid) filter_ = "({}={})".format("uid", uid)
log.debug("Search filter: {}".format(filter_)) log.debug("Search filter: {}".format(filter_))
response_id = client.connection.search( response_id = client.connection.search(
client.base_dn, filter_, client.base_dn, filter_, search_scope="SUBTREE", attributes=[ALL_ATTRIBUTES]
search_scope='SUBTREE',
attributes=[ALL_ATTRIBUTES]
) )
response = get_response(client, response_id) response = get_response(client, response_id)
@ -24,9 +22,10 @@ def get_user_by_uid(client, uid):
return None return None
if len(response) > 1: if len(response) > 1:
log.error("Looking for exactly one result but server gave {}. " log.error(
"Taking the first and ignoring the rest." "Looking for exactly one result but server gave {}. "
.format(len(response))) "Taking the first and ignoring the rest.".format(len(response))
)
return make_user_dict(client, response[0]) return make_user_dict(client, response[0])
@ -34,13 +33,11 @@ def get_user_by_uid(client, uid):
def get_all_users(client): def get_all_users(client):
log.info("Searching all the users") log.info("Searching all the users")
dn = 'ou=Hackers,{}'.format(client.base_dn) dn = "ou=Hackers,{}".format(client.base_dn)
log.debug("Search dn: {}".format(dn)) log.debug("Search dn: {}".format(dn))
response_id = client.connection.search( response_id = client.connection.search(
dn, '(objectclass=person)', dn, "(objectclass=person)", search_scope="SUBTREE", attributes=[ALL_ATTRIBUTES]
search_scope='SUBTREE',
attributes=[ALL_ATTRIBUTES]
) )
response = get_response(client, response_id) response = get_response(client, response_id)
@ -50,28 +47,24 @@ def get_all_users(client):
def add_user(client, uid, cn, sn, mail, password): def add_user(client, uid, cn, sn, mail, password):
dn = 'uid={},ou=Hackers,{}'.format(uid, client.base_dn) dn = "uid={},ou=Hackers,{}".format(uid, client.base_dn)
hashed_password = hashed(HASHED_SALTED_SHA, password) hashed_password = hashed(HASHED_SALTED_SHA, password)
attributes={ attributes = {
'objectClass': [ "objectClass": ["inetOrgPerson", "organizationalPerson", "person", "top"],
'inetOrgPerson', "cn": cn,
'organizationalPerson', "sn": sn,
'person', 'top' "mail": mail,
], "userPassword": hashed_password,
'cn': cn,
'sn': sn,
'mail': mail,
'userPassword': hashed_password
} }
add_entry(client, dn, attributes) add_entry(client, dn, attributes)
def delete_user(client, user): def delete_user(client, user):
delete_entry(client, user['dn']) delete_entry(client, user["dn"])
def delete_user_by_uid(client, uid): def delete_user_by_uid(client, uid):
dn = 'uid={},ou=Hackers,{}'.format(uid, client.base_dn) dn = "uid={},ou=Hackers,{}".format(uid, client.base_dn)
delete_entry(client, dn) delete_entry(client, dn)

View File

@ -5,37 +5,35 @@ log = get_logger(__name__)
def make_user_dict(client, entry): def make_user_dict(client, entry):
attributes = entry['attributes'] attributes = entry["attributes"]
user = {} user = {}
user['uid'] = attributes['uid'][0] user["uid"] = attributes["uid"][0]
user['dn'] = 'uid={},ou=Hackers,{}'.format(user['uid'], client.base_dn) user["dn"] = "uid={},ou=Hackers,{}".format(user["uid"], client.base_dn)
user['cn'] = attributes['cn'][0] user["cn"] = attributes["cn"][0]
user['sn'] = attributes['sn'][0] user["sn"] = attributes["sn"][0]
user['mail'] = attributes['mail'][0] user["mail"] = attributes["mail"][0]
user['password'] = attributes['userPassword'][0] user["password"] = attributes["userPassword"][0]
return user return user
def get_uid_from_dn(client, dn): def get_uid_from_dn(client, dn):
uid = re.search('uid=(.+?),ou=Hackers,{}'.format(client.base_dn), uid = re.search("uid=(.+?),ou=Hackers,{}".format(client.base_dn), dn).group(1)
dn).group(1)
return uid return uid
def make_group_dict(client, entry): def make_group_dict(client, entry):
attributes = entry['attributes'] attributes = entry["attributes"]
cn = attributes['cn'][0] cn = attributes["cn"][0]
dn = 'cn={},ou=Groups,{}'.format(cn, client.base_dn) dn = "cn={},ou=Groups,{}".format(cn, client.base_dn)
members = [get_uid_from_dn(client, u_dn) members = [get_uid_from_dn(client, u_dn) for u_dn in attributes["member"]]
for u_dn in attributes['member']]
group = {} group = {}
group['dn'] = dn group["dn"] = dn
group['cn'] = cn group["cn"] = cn
group['members'] = members group["members"] = members
return group return group
@ -49,22 +47,24 @@ def get_response(client, response_id):
log.debug("Response: {}".format(response)) log.debug("Response: {}".format(response))
log.debug("Result: {}".format(result)) log.debug("Result: {}".format(result))
if result['description'] is not 'success': if result["description"] is not "success":
raise Exception(result['description']) raise Exception(result["description"])
return response return response
def add_entry(client, dn, attributes): def add_entry(client, dn, attributes):
log.info('Adding entry with distinguiscet name: {}' log.info(
'and attributes {}'.format(dn, attributes)) "Adding entry with distinguiscet name: {}"
"and attributes {}".format(dn, attributes)
)
response_id = client.connection.add(dn, attributes=attributes) response_id = client.connection.add(dn, attributes=attributes)
response = get_response(client, response_id) response = get_response(client, response_id)
return response return response
def delete_entry(client, dn): def delete_entry(client, dn):
log.info('Deleting entry with distinguiscet name: {}') log.info("Deleting entry with distinguiscet name: {}")
response_id = client.connection.delete(dn) response_id = client.connection.delete(dn)
response = get_response(client, response_id) response = get_response(client, response_id)
return response return response