Compare commits
4 Commits
master
...
json-seria
Author | SHA1 | Date | |
---|---|---|---|
29685a66b5 | |||
2f2142025f | |||
8fbf159461 | |||
581270c6c1 |
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()}
|
|
||||||
|
|
|
@ -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"),
|
||||||
|
)
|
||||||
|
|
|
@ -2,16 +2,17 @@ import sys
|
||||||
import argparse
|
import argparse
|
||||||
import inspect
|
import inspect
|
||||||
from phi.logging import setup_logging, get_logger
|
from phi.logging import setup_logging, get_logger
|
||||||
|
from phi.compat.argparse import BooleanOptionalAction
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
subparses = parser.add_subparsers(title='actions', dest='action')
|
subparses = parser.add_subparsers(title="actions", dest="action")
|
||||||
|
|
||||||
cli_callbacks = {}
|
cli_callbacks = {}
|
||||||
|
|
||||||
|
|
||||||
def register(action_info='', param_infos=[]):
|
def register(action_info="", param_infos=[]):
|
||||||
def decorator(action):
|
def decorator(action):
|
||||||
# Get function name and arguments
|
# Get function name and arguments
|
||||||
action_name = action.__name__
|
action_name = action.__name__
|
||||||
|
@ -21,7 +22,7 @@ def register(action_info='', param_infos=[]):
|
||||||
subparser = subparses.add_parser(action_name, help=action_info)
|
subparser = subparses.add_parser(action_name, help=action_info)
|
||||||
|
|
||||||
for i, name in enumerate(param_names):
|
for i, name in enumerate(param_names):
|
||||||
info = param_infos[i] if i<len(param_infos) else ''
|
info = param_infos[i] if i < len(param_infos) else ""
|
||||||
subparser.add_argument(dest=name, help=info)
|
subparser.add_argument(dest=name, help=info)
|
||||||
|
|
||||||
# Register action
|
# Register action
|
||||||
|
@ -33,7 +34,7 @@ def register(action_info='', param_infos=[]):
|
||||||
|
|
||||||
def run(args):
|
def run(args):
|
||||||
for action_name, (action, param_names) in cli_callbacks.items():
|
for action_name, (action, param_names) in cli_callbacks.items():
|
||||||
if args['action'] == action_name:
|
if args["action"] == action_name:
|
||||||
action(**{pname: args[pname] for pname in param_names})
|
action(**{pname: args[pname] for pname in param_names})
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,6 +42,12 @@ def add_arg(name, example, info):
|
||||||
parser.add_argument(name, metavar=example, help=info)
|
parser.add_argument(name, metavar=example, help=info)
|
||||||
|
|
||||||
|
|
||||||
|
def add_flag(name, info):
|
||||||
|
parser.add_argument(
|
||||||
|
name, action=BooleanOptionalAction, help=info, required=False, default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
namespace = parser.parse_args(sys.argv[1:])
|
namespace = parser.parse_args(sys.argv[1:])
|
||||||
args = namespace.__dict__
|
args = namespace.__dict__
|
||||||
|
|
0
src/phi/compat/__init__.py
Normal file
0
src/phi/compat/__init__.py
Normal file
51
src/phi/compat/argparse.py
Normal file
51
src/phi/compat/argparse.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# WARNING: This code has been copied verbatim from
|
||||||
|
# https://github.com/python/cpython/blob/3.9/Lib/argparse.py
|
||||||
|
# To be removed when the minimum python supported version
|
||||||
|
# will be >= 3.9
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
class BooleanOptionalAction(argparse.Action):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
option_strings,
|
||||||
|
dest,
|
||||||
|
default=None,
|
||||||
|
type=None,
|
||||||
|
choices=None,
|
||||||
|
required=False,
|
||||||
|
help=None,
|
||||||
|
metavar=None,
|
||||||
|
):
|
||||||
|
|
||||||
|
_option_strings = []
|
||||||
|
for option_string in option_strings:
|
||||||
|
_option_strings.append(option_string)
|
||||||
|
|
||||||
|
if option_string.startswith("--"):
|
||||||
|
option_string = "--no-" + option_string[2:]
|
||||||
|
_option_strings.append(option_string)
|
||||||
|
|
||||||
|
if help is not None and default is not None:
|
||||||
|
help += f" (default: {default})"
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
option_strings=_option_strings,
|
||||||
|
dest=dest,
|
||||||
|
nargs=0,
|
||||||
|
default=default,
|
||||||
|
type=type,
|
||||||
|
choices=choices,
|
||||||
|
required=required,
|
||||||
|
help=help,
|
||||||
|
metavar=metavar,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
if option_string in self.option_strings:
|
||||||
|
setattr(namespace, self.dest, not option_string.startswith("--no-"))
|
||||||
|
|
||||||
|
def format_usage(self):
|
||||||
|
return " | ".join(self.option_strings)
|
|
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
96
src/phicli
96
src/phicli
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from pprint import pformat as pp
|
from pprint import pformat as pp
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
import json
|
||||||
|
|
||||||
from phi.config import get_config
|
from phi.config import get_config
|
||||||
from phi.logging import setup_logging, get_logger
|
from phi.logging import setup_logging, get_logger
|
||||||
|
@ -11,21 +12,36 @@ from phi.ldap.group import get_group_by_cn, get_all_groups, add_group_member
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
JSON_ENC = json.JSONEncoder()
|
||||||
|
SHOW_PASSWORDS = False
|
||||||
|
|
||||||
@cli.register('dispaly user fields', ['user identifier'])
|
|
||||||
|
def sanitize(obj):
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return [sanitize(el) for el in obj]
|
||||||
|
elif isinstance(obj, dict):
|
||||||
|
return dict((sanitize(k), sanitize(v)) for k, v in obj.items())
|
||||||
|
elif isinstance(obj, bytes):
|
||||||
|
return [hex(el) for el in list(obj)]
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
@cli.register("dispaly user fields", ["user identifier"])
|
||||||
def showuser(uid):
|
def showuser(uid):
|
||||||
user = get_user_by_uid(client, uid)
|
user = get_user_by_uid(client, uid)
|
||||||
if user is None:
|
if user is None:
|
||||||
print('User {} not found'.format(uid))
|
print("User {} not found".format(uid))
|
||||||
return
|
return
|
||||||
|
if not SHOW_PASSWORDS:
|
||||||
|
user.pop("password")
|
||||||
|
|
||||||
print(pp(user))
|
print(JSON_ENC.encode(sanitize(user)))
|
||||||
|
|
||||||
|
|
||||||
@cli.register('add a new user', ['user identifier'])
|
@cli.register("add a new user", ["user identifier"])
|
||||||
def adduser(uid):
|
def adduser(uid):
|
||||||
def ask(prompt, default):
|
def ask(prompt, default):
|
||||||
full_prompt = '{} [{}] '.format(prompt, default)
|
full_prompt = "{} [{}] ".format(prompt, default)
|
||||||
return input(full_prompt) or default
|
return input(full_prompt) or default
|
||||||
|
|
||||||
user = get_user_by_uid(client, uid)
|
user = get_user_by_uid(client, uid)
|
||||||
|
@ -33,98 +49,100 @@ def adduser(uid):
|
||||||
print("User {} already existing".format(uid))
|
print("User {} already existing".format(uid))
|
||||||
return
|
return
|
||||||
|
|
||||||
cn = ask('Common name:', uid)
|
cn = ask("Common name:", uid)
|
||||||
sn = ask('Last name:', uid)
|
sn = ask("Last name:", uid)
|
||||||
mail = ask('Mail:', '{}@localhost'.format(uid))
|
mail = ask("Mail:", "{}@localhost".format(uid))
|
||||||
|
|
||||||
password = getpass()
|
password = getpass()
|
||||||
pass_check = getpass('Retype password: ')
|
pass_check = getpass("Retype password: ")
|
||||||
if password != pass_check:
|
if password != pass_check:
|
||||||
print('Password not matching')
|
print("Password not matching")
|
||||||
return
|
return
|
||||||
|
|
||||||
add_user(client, uid, cn, sn, mail, password)
|
add_user(client, uid, cn, sn, mail, password)
|
||||||
|
|
||||||
# Check
|
# Check
|
||||||
user = get_user_by_uid(client, uid)
|
user = get_user_by_uid(client, uid)
|
||||||
print()
|
if not SHOW_PASSWORDS:
|
||||||
print(pp(user))
|
user.pop("password")
|
||||||
|
|
||||||
|
print(JSON_ENC.encode(sanitize(user)))
|
||||||
|
|
||||||
|
|
||||||
@cli.register('delete an user', ['user identifier'])
|
@cli.register("delete an user", ["user identifier"])
|
||||||
def deluser(uid):
|
def deluser(uid):
|
||||||
check = input('Are you sure? [y/N] ') or 'N'
|
check = input("Are you sure? [y/N] ") or "N"
|
||||||
if check.lower() != 'y':
|
if check.lower() != "y":
|
||||||
print('Ok then')
|
print("Ok then")
|
||||||
return
|
return
|
||||||
|
|
||||||
user = get_user_by_uid(client, uid)
|
user = get_user_by_uid(client, uid)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
delete_user(client, user)
|
delete_user(client, user)
|
||||||
print('Done')
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print('User {} not found'.format(uid))
|
print("User {} not found".format(uid))
|
||||||
|
|
||||||
|
|
||||||
@cli.register('show a group', ['group common name'])
|
@cli.register("show a group", ["group common name"])
|
||||||
def showgroup(cn):
|
def showgroup(cn):
|
||||||
group = get_group_by_cn(client, cn)
|
group = get_group_by_cn(client, cn)
|
||||||
if group is None:
|
if group is None:
|
||||||
print('Group {} not found'.format(gcn))
|
print("Group {} not found".format(gcn))
|
||||||
return
|
return
|
||||||
|
|
||||||
print(pp(group))
|
print(JSON_ENC.encode(sanitize(group)))
|
||||||
|
|
||||||
|
|
||||||
@cli.register('list all groups')
|
@cli.register("list all groups")
|
||||||
def listgroups():
|
def listgroups():
|
||||||
groups = get_all_groups(client)
|
groups = get_all_groups(client)
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
print(group['cn'])
|
print(group["cn"])
|
||||||
|
|
||||||
|
|
||||||
@cli.register('add an user to a group',
|
@cli.register("add an user to a group", ["user identifier", "group common name"])
|
||||||
['user identifier', 'group common name'])
|
|
||||||
def addtogroup(uid, gcn):
|
def addtogroup(uid, gcn):
|
||||||
user = get_user_by_uid(client, uid)
|
user = get_user_by_uid(client, uid)
|
||||||
group = get_group_by_cn(client, gcn)
|
group = get_group_by_cn(client, gcn)
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
print('User {} not found'.format(uid))
|
print("User {} not found".format(uid))
|
||||||
return
|
return
|
||||||
|
|
||||||
if group is None:
|
if group is None:
|
||||||
print('Group {} not found'.format(gcn))
|
print("Group {} not found".format(gcn))
|
||||||
return
|
return
|
||||||
|
|
||||||
if uid in group['members']:
|
if uid in group["members"]:
|
||||||
print('User {} is already in group {}'.format(uid, gcn))
|
print("User {} is already in group {}".format(uid, gcn))
|
||||||
return
|
return
|
||||||
|
|
||||||
add_group_member(client, group, user)
|
add_group_member(client, group, user)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
cli.add_arg('--config', 'config.yml', 'custom configuration file')
|
cli.add_arg("--config", "config.yml", "custom configuration file")
|
||||||
|
cli.add_flag("--show-passwords", "show the passwords bytes")
|
||||||
args = cli.get_args()
|
args = cli.get_args()
|
||||||
|
|
||||||
config_file = args['config']
|
config_file = args["config"]
|
||||||
|
SHOW_PASSWORDS = args["show_passwords"]
|
||||||
|
|
||||||
config_file, config = get_config(config_file)
|
config_file, config = get_config(config_file)
|
||||||
setup_logging(config.get('logging', {}))
|
setup_logging(config.get("logging", {}))
|
||||||
log.info("Using configuration at '{}':\n{}"
|
log.info("Using configuration at '{}':\n{}".format(config_file, pp(config)))
|
||||||
.format(config_file, pp(config)))
|
|
||||||
|
|
||||||
# TODO: check fields in config
|
# TODO: check fields in config
|
||||||
client = phi.ldap.client.Client(**config['ldap'])
|
client = phi.ldap.client.Client(**config["ldap"])
|
||||||
|
|
||||||
log.info('Opening LDAP client')
|
log.info("Opening LDAP client")
|
||||||
client.open()
|
client.open()
|
||||||
|
|
||||||
log.info('Arguments: {}'.format(pp(args)))
|
log.info("Arguments: {}".format(pp(args)))
|
||||||
|
|
||||||
cli.run(args)
|
cli.run(args)
|
||||||
|
|
||||||
log.info('Closing LDAP client')
|
log.info("Closing LDAP client")
|
||||||
client.close()
|
client.close()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user