import json import ldap import ldap.modlist import random from datetime import datetime from mandaye import config from mandaye.log import logger from mandaye.backends.default import storage_conn def cmp_reverse_last_connection_date(x, y): return -cmp(x[1]['lastConnectionDate'][0], y[1]['lastConnectionDate'][0]) class Association(object): """ association dictionary return by the following methods: { 'id': '', # identifier of your association (must be unique) 'sp_name': '', # name of the service provider (defined in the mappers) 'sp_login': '', # login on the service provider 'sp_post_values': '', # the post values for sp login form 'idp_unique_id:': '', # the unique identifier of the identity provider (ex.: a saml NameID) 'idp_name': '', # identity provide name 'last_connection': datetime.datetime, # last connection with this association 'creation_date': datetime.datetime, # creation date of this association } """ @staticmethod def ldap2association(ldap_object): return { 'id': ldap_object['uniqueID'][0], 'sp_name': ldap_object['spName'][0], 'sp_login': ldap_object['spLogin'][0], 'sp_post_values': json.loads(ldap_object['spPostValues'][0]), 'idp_unique_id': ldap_object['idpUniqueID'][0], 'idp_name': ldap_object['idpName'][0], 'last_connection': datetime.strptime( ldap_object['lastConnectionDate'][0][:14], '%Y%m%d%H%M%S'), 'creation_date': datetime.strptime( ldap_object['creationDate'][0][:14], '%Y%m%d%H%M%S'), } @staticmethod def get(sp_name, idp_unique_id, idp_name='default'): """ return a list of dict with associations matching all of this options """ associations = [] results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(spName=%s)(idpUniqueID=%s)(idpName=%s))' % (sp_name, idp_unique_id, idp_name)) for result in results: associations.append(Association.ldap2association(result[1])) return associations @staticmethod def get_by_id(asso_id): """ return a dict of the association with the id or None if it doesn't exist """ results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(uniqueID=%s))' %\ (asso_id)) if results: return Association.ldap2association(results[0][1]) return None @staticmethod def has_id(asso_id): """ check the given user is present in the directory """ results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(uniqueID=%s))' %\ (asso_id)) if results: return True return False @staticmethod def update_or_create(sp_name, sp_login, sp_post_values, idp_unique_id, idp_name='default', creation_date=None, last_connection_date=None): """ update or create an associtaion which match the following values return the association id """ results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(spName=%s)(spLogin=%s)(idpUniqueID=%s)(idpName=%s))' %\ (sp_name, sp_login, idp_unique_id, idp_name)) if not results: creation_date = creation_date or datetime.utcnow() last_connection_date = last_connection_date or datetime.utcnow() association = {'spName': str(sp_name), 'spLogin': str(sp_login), 'spPostValues': json.dumps(sp_post_values), 'idpUniqueID': str(idp_unique_id), 'idpName': str(idp_name), 'creationDate': creation_date.strftime('%Y%m%d%H%M%SZ'), 'lastConnectionDate': last_connection_date.strftime('%Y%m%d%H%M%SZ'), 'objectClass': 'MandayeUser' } mod_list = ldap.modlist.addModlist(association) while True: unique_id = random.randint(1, 5000000) dn = "uniqueID=%s,%s" % (unique_id, config.ldap_base_dn) try: result = storage_conn.add_s(dn, mod_list) except ldap.ALREADY_EXISTS: continue break logger.info("New association %r with %r", sp_login, idp_unique_id) return unique_id else: results.sort(cmp_reverse_last_connection_date) dn = results[0][0] mod_list = [(ldap.MOD_REPLACE, 'spPostValues', json.dumps(sp_post_values))] storage_conn.modify_s(dn, mod_list) logger.info("Update post values for %r (%r)", sp_login, idp_unique_id) return results[0][1]['uniqueID'][0] @staticmethod def delete(asso_id): """ delete the association which has the following asso_id """ dn = "uniqueID=%s,%s" % (asso_id, config.ldap_base_dn) storage_conn.delete_s(dn) logger.info('Delete %r association', dn) @staticmethod def get_last_connected(sp_name, idp_unique_id, idp_name='default'): """ get the last connecting association which match the parameters return a dict of the association """ results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(spName=%s)(idpUniqueID=%s)(idpName=%s))' % (sp_name, idp_unique_id, idp_name)) if results: results.sort(cmp_reverse_last_connection_date) return Association.ldap2association(results[0][1]) return None @staticmethod def update_last_connection(asso_id): """ update the association last connection time with the current time return a dict of the association """ last_connection = datetime.utcnow().strftime("%Y%m%d%H%M%SZ") dn = "uniqueID=%s,%s" % (asso_id, config.ldap_base_dn) mod_list = [(ldap.MOD_REPLACE, 'lastConnectionDate', last_connection)] storage_conn.modify_s(dn, mod_list) @staticmethod def has_sp_login(sp_login, sp_name): results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL, filterstr='(&(objectClass=MandayeUser)(spName=%s)(spLogin=%s))' %\ (sp_name, sp_login)) if results: return True return False