Add API for retrieving user info

make-things-work-brutally
crudo 2017-12-21 13:44:54 +01:00
parent 8f69c33de1
commit c79d89c0b7
11 changed files with 115 additions and 32 deletions

View File

@ -18,6 +18,8 @@ ldap:
password: phi
base_dn: dc=unit,dc=macaomilano,dc=org
attribute_id: uid
attribute_mail: mail
logging:
@ -44,5 +46,5 @@ logging:
level: DEBUG
handlers: [console, file]
ldap3:
level: DEBUG
level: WARNING
handlers: [console, file]

View File

@ -32,7 +32,7 @@ populate:
.PHONY: inspect
inspect:
ldapsearch -ZZ -h 127.0.0.1 \
ldapsearch -ZZ -H ldap://127.0.0.1 \
-x -D "cn=root,dc=unit,dc=macaomilano,dc=org" -w root \
-b "dc=unit,dc=macaomilano,dc=org" \
'(objectclass=*)'

View File

@ -1,12 +1,23 @@
from aiohttp.web import json_response
from aiohttp.web import json_response, View
from aiohttp.web import HTTPNotFound, HTTPUnprocessableEntity
from phi.logging import get_logger
from phi.ldap.user import get_user_by_uid
from phi.api.utils import serialize
log = get_logger(__name__)
def status(request):
response = {
'ldap': {
'connected': not request.app['ldap_client'].connection.closed,
'bound': request.app['ldap_client'].connection.bound
}
}
class User(View):
async def get(self):
uid = self.request.match_info.get('uid', None)
if uid is None:
return HTTPUnprocessableEntity()
return json_response(response)
client = self.request.app['ldap_client']
user = get_user_by_uid(client, uid)
if not user:
return HTTPNotFound()
return json_response(serialize(user))

View File

@ -1,8 +1,8 @@
from aiohttp.web import get
from aiohttp.web import route
from phi.api.rest import status
from phi.api.rest import User
api_routes = [
get('/status', status)
route('*', '/user/{uid}', User)
]

View File

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

View File

@ -10,7 +10,6 @@ def setup_app(config):
app = web.Application(loop=loop)
app['config'] = config
api = api_app(config)
app.add_subapp('/api', api)

View File

@ -1,30 +1,39 @@
from threading import Lock
from ldap3.utils.log import set_library_log_detail_level, BASIC
from ldap3.utils.log import set_library_log_detail_level, PROTOCOL
from phi.logging import get_logger
from phi.ldap.connection import make_connection
from phi.ldap.connection import open_connection, close_connection
log = get_logger(__name__)
set_library_log_detail_level(BASIC)
set_library_log_detail_level(PROTOCOL)
class Client:
def __init__(self, host=None, port=389,
encryption=None, ciphers=None, validate=False,
ca_certs=None, username=None, password=None,
base_dn=None):
def __init__(self,
host=None, port=389,
encryption=None, ciphers=None, validate=False, ca_certs=None,
username=None, password=None,
base_dn=None,
attribute_id='uid', attribute_mail='mail'):
log.info("Initializing LDAP Client.")
self.host = host
self.port = port
self.encryption = encryption
self.ciphers = ciphers
self.validate = validate
self.ca_certs = ca_certs
self.username = username
self.password = password
self.base_dn = base_dn
self.attribute_id = attribute_id
self.attribute_mail = attribute_mail
self.connection_lock = Lock()
self.connection = make_connection(host=self.host, port=self.port,
encryption=self.encryption,
@ -41,7 +50,8 @@ class Client:
self.connection_lock.release()
else:
self.connection_lock.release()
raise Exception("Trying to open a connection, but it is already open.")
raise Exception("Trying to open a connection, "
"but it is already open.")
def close(self):
self.connection_lock.acquire()

View File

@ -1,10 +0,0 @@
def inspect(client):
id = client.connection.search(client.base_dn,
'(objectclass=*)')
response, result = client.connection.get_response(id)
return response
def whoami(client):
response = client.connection.extend.standard.who_am_i()
return response

View File

@ -0,0 +1,36 @@
from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES
from phi.logging import get_logger
log = get_logger(__name__)
def get_entry_by_uid(client, uid):
log.info("Searching entry with identifier: {}".format(uid))
filter_ = "({}={})".format(client.attribute_id, uid)
log.debug("Search filter: {}".format(filter_))
response_id = client.connection.search(
client.base_dn, filter_,
search_scope='SUBTREE',
attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]
)
response, result, request = client.connection.get_response(
response_id, get_request=True
)
log.debug("Request: {}".format(request))
log.debug("Response: {}".format(response))
log.debug("Result: {}".format(result))
if not response:
return None
if response[1:]:
log.erorr("Looking for exactly one result but server gave {}. "
"Taking the first and ignoring the rest."
.format(len(response)))
return response[0]

View File

@ -0,0 +1,26 @@
from phi.ldap.entry import get_entry_by_uid
from phi.ldap.utils import flatten_attributes
def user_attributes_mapping(client):
return {
client.attribute_id: 'uid',
client.attribute_mail: 'mail',
'createTimestamp': 'created_at',
'modifyTimestamp': 'modified_at'
}
def get_user_by_uid(client, uid):
entry = get_entry_by_uid(client, uid)
if not entry:
return None
mapping = user_attributes_mapping(client)
user = {mapping[k]: v
for k, v in entry['attributes'].items()
if k in mapping.keys()}
return flatten_attributes(user)

View File

@ -0,0 +1,3 @@
def flatten_attributes(d):
return {k: (v[0] if isinstance(v, list) else v)
for k, v in d.items()}