Exemple #1
0
def _checkAndRemoveInLdap(reg, uid):
    '''
        chequea si existe un usuario y lo elimina
        obtiene el password y lo retorna
    '''
    logging.info('Conectandose al server ldap en {}'.format(reg.get('ldap_server')))
    from ldap3 import Server, Connection, ALL_ATTRIBUTES
    s = Server(reg.get('ldap_server'))
    conn = Connection(s, user=reg.get('ldap_user'), password=reg.get('ldap_password'))
    conn.bind()
    try:
        logging.info('buscando usuario con uid = {}'.format(uid))
        if not conn.search('ou=people,dc=econo', '(uid={})'.format(uid, uid), attributes=ALL_ATTRIBUTES):
            return None

        userPassword = None
        for e in conn.entries:
            logging.info(e.entry_get_dn())
            if 'userPassword' in e:
                userPassword = e.userPassword

            logging.info('Elimando : {}'.format(e.entry_get_dn()))
            conn.delete(e.entry_get_dn())

        return userPassword

    finally:
        conn.unbind()
def _remove_all_in(connection: Connection, dn: str):
    try:
        connection.search(dn, '(objectClass=*)', search_scope=LEVEL)
    except LDAPNoSuchObjectResult:
        return

    for entry in connection.entries:
        connection.delete(entry.entry_dn)
def delete_user(cn):
    try:
        # 连接服务器
        c = Connection(server, auto_bind=True, user="******"+username, password=password)

        c.delete(cn)

        return c.result

    except Exception:
        return None
Exemple #4
0
 def ldap_del(self, cnoudel):
     """
     ldap_del ; shan chu ldap shu ju
     :param cnoudel:
     :return:
     """
     server = Server(LDAP_IP, port=LDAP_port, get_info=ALL)
     conn = Connection(server, user=LDAP_user, password=LDAP_pwd)
     conn.open()
     conn.bind()
     conn.delete(cnoudel + "," + ADMIN_DN)
     conn.unbind()
Exemple #5
0
    def create_user(self, AD_SERVER, OU_BASE, DOMAIN, BIND_USERNAME,
                    BIND_PASSWORD, NEW_USER, NEW_USER_PASWORD, DESCRIPTION):

        tlsconfig = Tls(validate=ssl.CERT_NONE)
        server = Server(AD_SERVER,
                        port=636,
                        use_ssl=True,
                        tls=tlsconfig,
                        get_info=ALL)

        print(AD_SERVER, OU_BASE, DOMAIN, BIND_USERNAME, BIND_PASSWORD,
              NEW_USER)

        try:
            conn = Connection(server, BIND_USERNAME, BIND_PASSWORD)
            conn.start_tls()
            if not conn.bind():
                return 'Bind ERROR {}'.format(conn.result)

            temp_user_dn = 'cn=' + NEW_USER + ',' + OU_BASE
            print(temp_user_dn)

            conn.add(
                temp_user_dn, 'user', {
                    'givenName': NEW_USER,
                    'description': 'Create Time : ' + time.ctime(),
                    'company': DESCRIPTION,
                    'samaccountname': NEW_USER,
                    'userPrincipalName': NEW_USER + '@' + DOMAIN
                })
            if conn.result['result'] != 0:
                conn.unbind
                return conn.result
            print(conn.result)

            conn.extend.microsoft.modify_password(temp_user_dn,
                                                  NEW_USER_PASWORD)
            if conn.result['result'] != 0:
                conn.delete(temp_user_dn)
                conn.unbind
                return conn.result
            print(conn.result)

            conn.modify(temp_user_dn,
                        {'userAccountControl': [(MODIFY_REPLACE, ['512'])]})

        except Exception as e:
            return 'Create User Error : {}'.format(e)

        conn.unbind()
        return conn.result
Exemple #6
0
def delete_user(username):
    try:
        # 连接服务器
        c = Connection(server,
                       auto_bind=True,
                       user="******" + ad_admin_username,
                       password=ad_admin_password)

        c.delete(get_user_info(username).get('dn'))

        return True

    except Exception:
        return False
Exemple #7
0
 def ldap_connection(self):
     total_entries = 0
     server = Server('ipa.demo1.freeipa.org', get_info=ALL)
     conn = Connection(server, 'uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org', 'Secret123', auto_bind=True)
     conn.search('dc=demo1,dc=freeipa,dc=org', '(objectclass=person)')
     search=conn.entries
     print("data",search)
     conn.add('ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'organizationalUnit')
     # Add a new user
     conn.add('cn=b.young,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'inetOrgPerson', {'givenName': 'Beatrix', 'sn': 'Young', 'departmentNumber': 'DEV', 'telephoneNumber': 1111})
     detail=server.schema.object_classes['inetOrgPerson']
     conn.search('ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', '(cn=b.young)', attributes=['objectclass', 'sn', 'cn', 'givenname'])
     add=conn.entries[0]
     print(add)
     conn.modify_dn('cn=b.young,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'cn=b.smith')
     conn.search('ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', '(cn=b.smith)', attributes=['objectclass', 'sn', 'cn', 'givenname'])
     modify=conn.entries[0]
     print(modify)
     total_entries += len(conn.response)
     for entry in conn.response:
         print(entry['dn'], entry['attributes'])
     # perform a Delete operation
     message_id = conn.delete('cn=b.young,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org')
     # perform the Abandon operation
     delete= conn.abandon(message_id)
Exemple #8
0
def delAll():
    try:
        ldap_uri = "ldaps://{}".format(host)
        print('Connecting to ', host)
        server = Server(ldap_uri, use_ssl=True)
        conn = Connection(server, DN, password)

        conn.bind()
        conn.search(search_base = 'o=gluu',
        search_filter = '(&(cn=*)(!(uid=admin)))',
        search_scope = SUBTREE,
        attributes=ALL_ATTRIBUTES)

        for entry in conn.response:
            print(entry['dn'])
            conn.delete(entry['dn'])
        conn.unbind()

    except:
        print("Cannot connect to host.")
Exemple #9
0
 def delete(self):
     result = False
     dn = Config.LDAP_DN.replace(Config.LDAP_UID, self._uid)
     server = Server(Config.LDAP, use_ssl=True, get_info=ALL)
     try:
         conn = Connection(server=server,
                           user=Config.LDAP_ADMIN,
                           password=Config.LDAP_ADMIN_PASSWORD,
                           auto_bind=True,
                           receive_timeout=30)
         result = conn.delete(dn)
         conn.unbind()
     except Exception as e:
         logger.error(e)
     return result
class Test(unittest.TestCase):
    def setUp(self):
        server = Server(host=test_server, port=test_port, allowed_referral_hosts=('*', True))
        self.connection = Connection(server, auto_bind=True, version=3, client_strategy=test_strategy, user=test_user, password=test_password, authentication=test_authentication, lazy=test_lazy_connection, pool_name='pool1')
        result = self.connection.add(dn_for_test(test_base, 'test-add-for-delete'), [], {'objectClass': 'iNetOrgPerson', 'sn': 'test-add'})
        if not isinstance(result, bool):
            self.connection.get_response(result)

    def tearDown(self):
        self.connection.unbind()
        if self.connection.strategy_type == STRATEGY_REUSABLE_THREADED:
            self.connection.strategy.terminate()
        self.assertFalse(self.connection.bound)

    def test_delete(self):
        result = self.connection.delete(dn_for_test(test_base, 'test-add-for-delete'))
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject'])
class Test(unittest.TestCase):
    def setUp(self):
        server = Server(host=test_server, port=test_port, allowed_referral_hosts=('*', True), get_info=test_get_info)
        self.connection = Connection(server, auto_bind=True, version=3, client_strategy=test_strategy, user=test_user, password=test_password, authentication=test_authentication, lazy=test_lazy_connection, pool_name='pool1', check_names=test_check_names)
        result = self.connection.delete(dn_for_test(test_base, 'test-add-operation'))
        if not isinstance(result, bool):
            self.connection.get_response(result)

    def tearDown(self):
        self.connection.unbind()
        if self.connection.strategy_type == STRATEGY_REUSABLE_THREADED:
            self.connection.strategy.terminate()
        self.assertFalse(self.connection.bound)

    @pytest.mark.skipif(True,  reason="needs rework")
    def test_add(self):
        result = self.connection.add(dn_for_test(test_base, 'test-add-operation'), 'iNetOrgPerson', {'objectClass': 'iNetOrgPerson', 'sn': 'test-add', test_name_attr: 'test-add-operation'})
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result

        self.assertTrue(result['description'] in ['success', 'entryAlreadyExists'])
Exemple #12
0
class OptLdap():
    """ AD中的用户与组织单位操作 """
    def __init__(self):
        """ 连接初始化 """
        self.connect = Connection( # 配置服务器连接参数
            server=AD_SERVER_POOL,
            auto_bind=True,
            authentication=NTLM, # 连接Windows AD 使用NTLM方式认证
            user=SERVER_USER,
            password=SERVER_PASSWORD,
        )
        ldap_logger.info("连接AD域服务器 %s", self.connect)
        self.leaved_base_dn = 'OU=LEAVED,DC=sh,DC=hupu,DC=com' # 离职账户的OU
        self.active_base_dn = 'OU=HUPU,DC=sh,DC=hupu,DC=com' # 在职账户的OU
        self.all_base_dn = 'DC=sh,DC=hupu,DC=com' # 所有用户的OU
        self.user_search_filter = '(objectclass=user)' # 只获取用户对象
        self.ou_search_filter = '(objectclass=organizationalUnit)' # 只获取OU对象
        self.attributes_ou = ['Name', 'ObjectGUID']
        self.attributes_user = ['name', 'memberOf', 'sAMAccountName', 'badPwdCount', 'displayName', 'mail', 'userAccountControl', 'userPrincipalName', 'telephoneNumber']

    def get_users(self, get_type='active'):
        """ 获取用户信息 """
        if get_type == 'all':
            self.connect.search(search_base=self.all_base_dn, search_filter=self.user_search_filter, attributes=self.attributes_user)
        elif get_type == 'leaved':
            self.connect.search(search_base=self.leaved_base_dn, search_filter=self.user_search_filter, attributes=self.attributes_user)
        else:
            self.connect.search(search_base=self.active_base_dn, search_filter=self.user_search_filter, attributes=self.attributes_user)
        res = json.loads(self.connect.response_to_json())['entries']
        ldap_logger.info("获取所有用户信息 %s - %s", get_type, self.connect.result)
        return res

    def get_obj_info(self, filter_key=None, filter_value=None, filter_all=None, attr=None):
        """ 根据自定义filter获取用户信息 """
        if filter_all:
            search_filter = filter_all
        else:
            search_filter = "(" + filter_key + "=" + filter_value + ")"
        res = []
        attr = attr if attr else ALL_ATTRIBUTES
        try:
            self.connect.search(search_base=self.all_base_dn, search_filter=search_filter, attributes=attr)
            res = json.loads(self.connect.response_to_json())['entries']
        except exceptions.LDAPException as ept:
            ldap_logger.error("获取自定义用户信息失败 %s", ept)
            raise
        # finally:
        #     self.connect.unbind()
        ldap_logger.info("获取自定义用户信息 %s - %s", search_filter, self.connect.result)
        return res

    def get_ous(self):
        """ 获取OU信息 """
        self.connect.search(search_base=self.active_base_dn, search_filter=self.ou_search_filter, attributes=self.attributes_ou)
        res = json.loads(self.connect.response_to_json())['entries']
        ldap_logger.info("获取所有OU信息 %s - %s", self.active_base_dn, self.connect.result)
        return res

    def del_obj(self, dn): # pylint: disable=invalid-name
        """
        删除用户 or 部门
        :param dn: 'CN=张三,OU=IT组,OU=企业信息化部,OU=虎扑,DC=sh,DC=hupu,DC=com' or 'OU=IT组,OU=企业信息化部,OU=虎扑,DC=sh,DC=hupu,DC=com'
        :return True/False
        """
        res = self.connect.delete(dn=dn)
        ldap_logger.info("用户信息 %s - %s", dn, res)
        return res, self.connect.result

    def create_obj(self, dn, obj_type, pwd="Abcd.1234", attr=None):  # pylint: disable=invalid-name
        """
        新增用户 or OU
        :param DN:  ou - "OU=IT组,OU=企业信息化部,OU=虎扑,DC=sh,DC=hupu,DC=com" or user - "CN=张三,OU=IT组,OU=企业信息化部,OU=虎扑,DC=sh,DC=hupu,DC=com"
        :param obj_type: user or ou
        :param attr: user - {"sAMAccountName": "zhangsan",
                            "Sn": "张",
                            "name":"张三",
                            "UserPrincipalName": "*****@*****.**",
                            "Mail": "*****@*****.**",
                            "Displayname": "张三"}
                    ou - {"name": "IT组"}
        :return : {"status": True/False, "msg": {}}
        """
        res = False
        ldap_logger.info("创建AD域Object %s - %s_attr:%s", dn, obj_type, attr)
        object_class = {'user': ['top', 'person', 'organizationalPerson', 'user'], 'ou': ['top', 'organizationalUnit']}
        try:
            res = self.connect.add(dn=dn, object_class=object_class[obj_type], attributes=attr)
            ldap_logger.info("创建Object结果 %s - %s - %s", dn, self.connect.result, res)
            msg = self.connect.result
            if obj_type == 'user': # 如果是用户,需要设置密码、激活账号
                self.connect.extend.microsoft.modify_password(dn, pwd)  # 设置密码
                self.connect.modify(dn, {'userAccountControl': [(MODIFY_REPLACE, 512)]})  # 设置账号状态,激活用户
        except exceptions.LDAPException as ept:
            ldap_logger.error("Object信息 %s - %s - %s", dn, self.connect, ept)
            msg = "Ldap新增Object操作失败,详细信息查看Log"
        # finally:
        #     self.connect.unbind()
        return res, msg

    def update_obj(self, dn, attr=None):  # pylint: disable=invalid-name
        """
        更新user or OU
        只允许OU更新name,user不能更新 ["name","sAMAccountName", "userPrincipalName", "displayname"]
        OU or USER都可以移动
        :param dn: 需要修改的完整DN
        :param attr: 需要更新的属性值,字典形式
        :return {"status: True/False, "msg": {'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'modDNResponse'}}
        """
        changes_dic = {}
        ldap_logger.info("更新Object的信息 %s - %s", dn, attr)
        for k, v in attr.items():  # pylint: disable=invalid-name
            if not self.compare_attr(dn=dn, attr=k, value=v):
                ldap_logger.info("对比Object信息结果 %s - %s:%s - %s", dn, k, v, self.connect.result)
                if k == "name":   # 修改name值只允许OU修改,不允许修改CN的name
                    res = self._rename_obj(dn=dn, newname=attr['name'])
                    if res['description'] == 'success':
                        if dn[:2] == "OU":
                            dn = "OU=%s,%s" % (attr["name"], dn.split(",", 1)[1])
                        else:
                            return {"status": False, "msg": "不支持的DN " + dn}
                elif k == "DistinguishedName":
                    res = self._move_object(dn=dn, new_dn=v) # 调用移动User or OU 的方法
                    if res['description'] == 'success':
                        if dn[:2] == "CN":
                            dn = "%s" % (attr["DistinguishedName"])
                        if dn[:2] == "OU":
                            dn = "%s" % (attr["DistinguishedName"])
                elif k in ["sAMAccountName", "userPrincipalName", "displayname"]:
                    return {"status": False, "msg": "不支持的属性 " + k}
                else:
                    changes_dic.update({k:[(MODIFY_REPLACE, [v])]})
                    self.connect.modify(dn=dn, changes=changes_dic)
        return {"status": True, "msg": self.connect.result}

    def _rename_obj(self, dn, newname):  # pylint: disable=invalid-name
        """
        OU or User 重命名方法
        :param dn:需要修改的object的完整dn路径
        :param newname: 新的名字
        :return:返回中有:'description': 'success', 表示操作成功
        {'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'modDNResponse'}
        """
        cn_ou = dn.split("=", 1)
        newname = cn_ou[0] + "=" + newname
        res = self.connect.modify_dn(dn, newname)
        ldap_logger.info("Remove-Object-Info %s - %s - %s", dn, self.connect.result, res)
        return self.connect.result


    def _move_object(self, dn, new_dn):  # pylint: disable=invalid-name
        """移动员工 or 部门到新部门"""
        relative_dn, superou = new_dn.split(",", 1)
        res = self.connect.modify_dn(dn=dn, relative_dn=relative_dn, new_superior=superou)
        ldap_logger.info("Move-Object-Info %s - %s - %s", dn, self.connect.result, res)
        return self.connect.result

    def leaved_user(self, dn):   # pylint: disable=invalid-name
        """ 处理离职的用户,离职用户放置在离职OU里,账号禁用 """
        res = self.connect.modify(dn, {'userAccountControl': [(MODIFY_REPLACE, 514)]})  # 设置账号状态,禁用账号
        ldap_logger.info("Leaved-User %s - Disable - %s", dn, res)
        if res:
            new_dn = dn.split(",")[0] + "," + self.leaved_base_dn
            res = self._move_object(dn=dn, new_dn=new_dn)
        return res

    def compare_attr(self, dn, attr, value):   # pylint: disable=invalid-name
        """比较员工指定的某个属性
        """
        try:
            res = self.connect.compare(dn=dn, attribute=attr, value=value)
            ldap_logger.info("Commpare-Object-Info %s - %s:%s - %s", dn, attr, value, res)
        except exceptions.LDAPException as ept:
            ldap_logger.error("Commpare-Object-Info-Expception %s - %s - %s", dn, self.connect, ept)
            res = False
        return res

    def reset_password(self, dn, new_pwd):  # pylint: disable=invalid-name
        """ 重置密码, 不需要原密码 """
        res = self.connect.extend.microsoft.modify_password(dn, new_pwd)
        ldap_logger.info("Reset-Password %s - %s", dn, self.connect.result)
        return res, self.connect.result
Exemple #13
0
class LDAPUserManager(object):

    def __init__(
            self,
            ldap_server,
            user_filter,
            base_dn=None,
            root_dn=None,
            passwd_dn=None,
            user_profile=[],
            read_only=True,
            cache_users=None,
            cache_groups=None):
        self.ldap_server = ldap_server
        self.root_dn = root_dn
        self.passwd_dn = passwd_dn
        self.base_dn = base_dn
        self.user_filter = user_filter
        self.user_profile = user_profile
        self.read_only = read_only
        self.cache_users = cache_users
        self.cache_groups = cache_groups
        self.ttl_users = 3660
        self.bind()


    def scopes_filter(self):
        f = 'ou=scopes,' + self.base_dn
        return f

    def scope_filter(self, scope):
        f = 'ou={scope},' + self.scopes_filter()
        return f.format(scope=scope)

    def groups_filter(self, scope):
        return 'ou=groups,' + self.scope_filter(scope=scope)

    def group_filter(self, scope, group):
        f = 'cn={group},' + self.groups_filter(scope=scope)
        return f.format(group=group)

    def roles_filter(self, scope):
        return 'ou=roles,' + self.scope_filter(scope=scope)

    def role_filter(self, scope, role):
        f = 'cn={role},' + self.roles_filter(scope)
        return f.format(role=role)

    def users_filter(self):
        f = 'ou=users,' + self.base_dn
        return f

    def userdn2id(self, user_dn):
        """
        :param user_dn: can be a user or a group dn
        :type user_dn: str
        """
        parse_dn = None
        if user_dn.startswith('mail='):
            parse_dn = 'mail='
        elif user_dn.startswith('cn='):
            parse_dn = 'cn='

        if parse_dn:
            return user_dn[len(parse_dn):user_dn.find(',')]

    def bind(self):
        if self.root_dn is None:
            raise Exception('No LDAP Admin Configuration')

        authentication_method = SIMPLE
        bind_type = ASYNC

        self.ldap_conn_mng = Connection(
            self.ldap_server,
            authentication=authentication_method,
            client_strategy=bind_type,
            user=self.root_dn,
            password=self.passwd_dn,
            read_only=self.read_only)
        self.ldap_conn_mng.bind()

    def bind_root_readonly(self):
        """
        A root connection to LDAP server only read_only
        """
        if self.root_dn is None:
            raise Exception('No LDAP Admin Configuration')

        authentication_method = SIMPLE
        bind_type = ASYNC

        ldap_conn_mng = Connection(
            self.ldap_server,
            authentication=authentication_method,
            client_strategy=bind_type,
            user=self.root_dn,
            password=self.passwd_dn,
            read_only=True)

        ldap_conn_mng.bind()
        return ldap_conn_mng

    def unbind(self):
        self.ldap_conn_mng.unbind()

    def checkUserName(self, username):
        if ',' in username:
            raise TypeError('No , in username')

    def parse_async_add(self, return_code):
        """
        Parser the response for an LDAP async ADD
        """
        result = 'Not done'
        if return_code:
            result = self.ldap_conn_mng.get_response(return_code)
            result = result[1].get('description', 'Error no description')
        return result

    async def addScope(self, scope):
        """
        !!!Admin add: must be protected when calling
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')

        self.bind()

        scope_dn = self.scope_filter(scope=scope)
        done = self.ldap_conn_mng.add(scope_dn, 'organizationalUnit')

        result = self.parse_async_add(done)

        if result == 'success':
            #add subelements for scope
            roles_dn = self.roles_filter(scope=scope)
            done = self.ldap_conn_mng.add(roles_dn, 'organizationalUnit')
            if self.parse_async_add(done) != 'success':
                result = 'Failed creating roles'

            groups_dn = self.groups_filter(scope=scope)
            done = self.ldap_conn_mng.add(groups_dn, 'organizationalUnit')
            if self.parse_async_add(done) != 'success':
                result = 'Failed creating groups'

        self.unbind()
        return result

    def addUser(self, username, password):
        """
        !!!Admin add: must be protected when calling
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')

        self.bind()

        self.checkUserName(username)
        user_dn = self.user_filter.format(username=username)
        if '@' in username:
            sn = username.split('@')[0]
        else:
            sn = username

        done = self.ldap_conn_mng.add(
            user_dn,
            self.user_profile,
            {
            'cn': username,
            'sn': sn,
            'mail': username,
            'userPassword': password,
            })

        result = self.parse_async_add(done)
        self.unbind()
        return result

    async def addGroup(self, scope, group):
        """
        !!!Admin add: must be protected when calling
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')

        self.bind()

        self.checkUserName(group)
        group_dn = self.group_filter(scope=scope, group=group)

        done = self.ldap_conn_mng.add(
            group_dn,
            'groupOfUniqueNames',
            {
            'cn': group,
            'uniqueMember': group_dn #  group itself as an empty members list
            })

        result = self.parse_async_add(done)
        self.unbind()
        return result

    async def setPassword(self, username, password):
        """
        !!!Admin add: must be protected when calling
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')
        self.bind()
        user_dn = self.user_filter.format(username=username)
        hashed_password = hashed(HASHED_SHA512, password)
        done = self.ldap_conn_mng.modify(
            user_dn,
            {'userPassword': [(MODIFY_REPLACE, [hashed_password])]}
            )
        result = self.parse_async_add(done)
        if result == 'success':
            return True
        else:
            return False

    async def addScopeRole(self, scope, user_dn, role):
        """
        !!!Admin add: must be protected when calling

        `user_dn` can be a user or a group dn.
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')

        self.bind()

        role_dn = self.role_filter(scope=scope, role=role.lower())

        #Create role for first time
        done = self.ldap_conn_mng.add(
            role_dn,
            'groupOfUniqueNames',
            {'cn': role, 'uniqueMember': user_dn}
            )
        result = self.parse_async_add(done)
        if  result == 'entryAlreadyExists':
            #Extend role with new user
            done = self.ldap_conn_mng.modify(
                role_dn,
                {'uniqueMember': [(MODIFY_ADD, [user_dn])]}
                )
            result = self.parse_async_add(done)

        self.unbind()
        return result

    async def addScopeRoleUser(self, scope, username, role):
        user_dn = self.user_filter.format(username=username)
        return await self.addScopeRole(scope, user_dn, role)

    async def addScopeRoleGroup(self, scope, groupname, role):
        group_dn = self.group_filter(scope, groupname)
        return await self.addScopeRole(scope, group_dn, role)

    async def delScopeRole(self, scope, username, role):
        """
        !!!Admin del: must be protected when calling
        """
        # Needs admin
        if self.read_only:
            raise Exception('LDAP in Read only mode')

        self.bind()

        role_dn = self.role_filter(scope=scope, role=role.lower())
        user_dn = self.user_filter.format(username=username)

        #Find role for first time
        done = self.ldap_conn_mng.modify(
            role_dn,
            {'uniqueMember': [(MODIFY_DELETE, [user_dn])]}
            )
        result = self.parse_async_add(done)

        if result == 'objectClassViolation':
            # member was the last remaining
            done = self.ldap_conn_mng.delete(role_dn)
            result = self.parse_async_add(done)

        self.unbind()
        return result

    async def searchUser(self, scope, criteria, exact_match, attrs, page=None, num_x_page=0):
        """ !!!Admin search: must be protected when calling
        """
        total = 0
        result = []
        paged_size = num_x_page
        paged_cookie = page

        if exact_match is False and any(criteria.values()):
            for k in criteria.keys():
                criteria[k] = "*" + criteria[k] + "*"

        for objclass in self.user_profile:
            criteria['objectClass'] = objclass

        filter_ldap = ""
        for j, v in criteria.items():
            filter_ldap += "(%s=%s)" % (j, v)
        filter_ldap = "(&%s)" % filter_ldap

        self.ldap_conn_mng.bind()
        done = self.ldap_conn_mng.search(
            self.base_dn,
            filter_ldap,
            search_scope=SUBTREE,
            dereference_aliases=DEREF_ALWAYS,
            attributes=USER_ATTRIBUTES,
            size_limit=0,
            time_limit=0,
            types_only=False,
            get_operational_attributes=False,
            controls=None,
            paged_size=paged_size,
            paged_criticality=False,
            paged_cookie=paged_cookie)

        if done:
            result = self.ldap_conn_mng.get_response(done)[0]
            total = len(result)
        self.ldap_conn_mng.unbind()
        return [dict(r['attributes']) for r in result], total

    async def getUser(self, dn, ldap_conn):
        # We should be logged in with the user
        with (await self.cache_users) as redis:
            user = await redis.get(dn)
            if user and user != b'{}':
                return ujson.loads(user)
        r = ldap_conn.search(
            dn,
            '(objectClass=*)',
            search_scope=BASE,
            attributes=USER_ATTRIBUTES)
        if r:
            res = ldap_conn.response[0]
            with (await self.cache_users) as redis:
                redis.set(res['dn'], ujson.dumps(dict(res['attributes'])))
                redis.expire(res['dn'], self.ttl_users)
            return res['attributes']

    async def loginUser(self, username, password):
        user_dn = self.user_filter.format(username=username)
        bind_type = SYNC
        authentication_method = SIMPLE
        ldap_conn = Connection(
            self.ldap_server,
            authentication=authentication_method,
            client_strategy=bind_type,
            user=user_dn,
            password=password,
            read_only=True)
        try:
            result = ldap_conn.bind()
            if result:
                user = await self.getUser(user_dn, ldap_conn)
                ldap_conn.unbind()
                return user
            else:
                return None
        except LDAPException:
            return None

    async def getUserName(self, username):
        dn = self.user_filter.format(username=username)
        with (await self.cache_users) as redis:
            user = await redis.get(dn)
            if user and user != b'{}':
                return ujson.loads(user)
        ldap_conn = self.bind_root_readonly()
        r = ldap_conn.search(
            dn,
            '(objectClass=*)',
            search_scope=BASE,
            attributes=USER_ATTRIBUTES)
        if r:
            names = ldap_conn.get_response(r)[0]
            res = [res['attributes']['cn'][0] for res in names]
        else:
            res = []
        ldap_conn.unbind()
        return ' '.join(res)

    async def get_user_groups(self, ldap_conn, scope, user_dn):
        """
        Return all groups cn that `user_dn` has in `scope`

        :param user_dn: can be a user or a group dn
        :type user_dn: str
        """
        groups_dn = self.groups_filter(scope=scope)

        search_filter = '(uniqueMember={0})'.format(user_dn)

        r = ldap_conn.search(
            groups_dn,
            search_filter,
            search_scope=SUBTREE,
            attributes=['cn']
        )
        if r:
            groups = ldap_conn.get_response(r)[0]
            groups = filter(lambda x: x['dn'] != user_dn, groups) # filter self
            return [res['attributes']['cn'][0] for res in groups]

    async def get_user_roles(self, ldap_conn, scope, user_dn, groups=None):
        """
        Return all roles cn that `user_dn` has in `scope`.
        Return all roles cn that each of `groups` has in `scope`.

        :param user_dn: can be a user or a group dn
        :type user_dn: str
        :param groups: (Optionally)
        :type groups: list
        """
        roles_dn = self.roles_filter(scope=scope)
        search_filter = '(uniqueMember={0})'.format(user_dn)

        if groups is not None:
            search_filter = '(|' + search_filter
            for group_cn in groups:
                group_dn = self.group_filter(scope=scope, group=group_cn)
                search_filter += '(uniqueMember={0})'.format(group_dn)
            search_filter += ')'


        r = ldap_conn.search(
            roles_dn,
            search_filter,
            search_scope=SUBTREE,
            attributes=['cn']
            )
        if r:
            roles = ldap_conn.get_response(r)[0]
            return [res['attributes']['cn'][0] for res in roles]

    async def get_info_user_or_group(self, user_dn, scope):
        """
        !!!Admin search: must be protected when calling

        :returns:
            {
                'groups': {
                    'group1': 1,
                },
                'roles': {
                    'plone.Manager': 1,
                }
            }
        """
        ldap_conn = self.bind_root_readonly()
        groups = await self.get_user_groups(ldap_conn, scope, user_dn)
        roles = await self.get_user_roles(
            ldap_conn,
            scope,
            user_dn,
            groups=groups,
        )
        ldap_conn.unbind()

        return {
            'roles': {e: 1 for e in roles},
            'groups': {e: 1 for e in groups},
        }

    async def getUserInfo(self, username, scope):
        """
        !!!Admin search: must be protected when calling

        :returns:
            {
                'groups': {
                    'group1': 1,
                },
                'roles': {
                    'plone.Manager': 1,
                }
                'name': 'Name'
            }
        """
        user_dn = self.user_filter.format(username=username)
        info = await self.get_info_user_or_group(user_dn, scope)
        info['name'] = username
        return info

    async def getGroupInfo(self, scope, group=None):
        """
        !!!Admin search: must be protected when calling

        :rtype: dict or list of dict or None
        :returns:
            {
                'members': [
                    member1,
                ],
                'name': 'Name'
            }

            or list of groups if group is None
            or None if group is not found

        """
        ldap_conn = self.bind_root_readonly()

        groups_dn = self.groups_filter(scope=scope)

        if group is None:
            search_filter = '(objectClass=groupOfUniqueNames)'
        else:
            search_filter = '(cn={0})'.format(group)

        r = ldap_conn.search(
            groups_dn,
            search_filter,
            search_scope=SUBTREE,
            attributes=['cn', 'uniqueMember']
            )

        if not r:
            raise Exception('LDAP Group search bad formed')

        groups = ldap_conn.get_response(r)[0]
        ldap_conn.unbind()

        async def ldap2json(entry):
            group_dn = entry['dn']
            group_name = entry['attributes']['cn'][0]
            members_ldap = entry['attributes']['uniqueMember']
            members_ldap = filter(lambda x: x != group_dn, members_ldap) # filter self
            info = await self.get_info_user_or_group(group_dn, scope)
            info.update({
                'name': group_name,
                'members': list(map(self.userdn2id, members_ldap)),
                })
            return info

        groups = await asyncio.gather(*map(ldap2json, groups))

        if group is None:
            return groups
        try:
            return groups[0]
        except IndexError:
            return None

    async def get_all_scopes(self, ldap_conn):
        """
        """
        r = ldap_conn.search(
            self.scopes_filter(),
            "(objectClass=organizationalUnit)",
            search_scope=LEVEL,
            attributes=['ou']
            )
        if r:
            scopes = ldap_conn.get_response(r)[0]
            return [scope['attributes']['ou'][0] for scope in scopes]

    async def getUserScopes(self, username):
        """
        Aquesta crida retorna tots els scopes als quals pertany un usuari

        Nota: es pot millorar. Recorre dos cops tots els scopes. Un cop per
        obtenir-los tots i un altre per filtrar segons si l'usuari pertany o no
        """
        ldap_conn = self.bind_root_readonly()

        all_scopes = await self.get_all_scopes(ldap_conn)

        if plone.oauth.is_superuser(username):
            scopes = all_scopes
        else:
            user_dn = self.user_filter.format(username=username)
            scopes = []
            for scope in all_scopes:
                roles = await self.get_user_roles(ldap_conn, scope, user_dn)
                if roles:
                    scopes.append(scope)

        ldap_conn.unbind()

        return {
            'scopes': scopes
        }

    async def get_all_users(self, ldap_conn):
        """
        Return all users cn that `username` has in `scope`.
        Optionally also search by `groups`.
        """
        r = ldap_conn.search(
            self.users_filter(),
            "(objectClass=organizationalPerson)",
            search_scope=LEVEL,
            attributes=['mail']
            )
        if r:
            users = ldap_conn.get_response(r)[0]
            return [user['attributes']['mail'][0] for user in users]

    async def getScopeUsers(self, scope):
        """
        Retorna tots els usuaris que pertanyen a un scope

        Nota: es pot millorar. Recorre dos cops tots els usuaris. Un cop per
        obtenir-los tots i un altre per filtrar segons si l'usuari pertany o no
        """
        ldap_conn = self.bind_root_readonly()

        all_users_ids = await self.get_all_users(ldap_conn)

        users = []
        for user_id in all_users_ids:
            user_dn = self.user_filter.format(username=user_id)
            roles = await self.get_user_roles(ldap_conn, scope, user_dn)
            if roles:
                user = {
                    'id': user_id,
                    'roles': roles
                }
                users.append(user)

        ldap_conn.unbind()

        return {
            'users': users
        }
Exemple #14
0
# define the server

s = Server(host='yourhostname.com', port=389, use_ssl=False, get_info='ALL')

# define the connection
c = Connection(s,
               user='******',
               password='******',
               version=3,
               authentication='SIMPLE',
               client_strategy='SYNC',
               read_only=False,
               raise_exceptions=True)

if not c.bind():
    print('error in binding', c.result)
else:
    print('Bind is successful!!')

result = c.delete('c=au,dc=yourhostname,dc=com')

if not result:
    print("Failed to delete")
else:
    print("Successfully deleted")

c.unbind()

print("Unbinded successful!!")
Exemple #15
0
def main():
    parser = argparse.ArgumentParser(
        description=
        'Query/modify DNS records for Active Directory integrated DNS via LDAP'
    )
    parser._optionals.title = "Main options"
    parser._positionals.title = "Required options"

    #Main parameters
    #maingroup = parser.add_argument_group("Main options")
    parser.add_argument(
        "host",
        type=str,
        metavar='HOSTNAME',
        help="Hostname/ip or ldap://host:port connection string to connect to")
    parser.add_argument("-u",
                        "--user",
                        type=str,
                        metavar='USERNAME',
                        help="DOMAIN\\username for authentication.")
    parser.add_argument(
        "-p",
        "--password",
        type=str,
        metavar='PASSWORD',
        help="Password or LM:NTLM hash, will prompt if not specified")
    parser.add_argument(
        "--forest",
        action='store_true',
        help="Search the ForestDnsZones instead of DomainDnsZones")
    parser.add_argument(
        "--legacy",
        action='store_true',
        help="Search the System partition (legacy DNS storage)")
    parser.add_argument(
        "--zone",
        help="Zone to search in (if different than the current domain)")
    parser.add_argument(
        "--print-zones",
        action='store_true',
        help=
        "Only query all zones on the DNS server, no other modifications are made"
    )
    parser.add_argument("--tcp", action='store_true', help="use DNS over TCP")

    recordopts = parser.add_argument_group("Record options")
    recordopts.add_argument("-r",
                            "--record",
                            type=str,
                            metavar='TARGETRECORD',
                            help="Record to target (FQDN)")
    recordopts.add_argument(
        "-a",
        "--action",
        choices=[
            'add', 'modify', 'query', 'remove', 'resurrect', 'ldapdelete'
        ],
        default='query',
        help="Action to perform. Options: add (add a new record), modify ("
        "modify an existing record), query (show existing), remove (mark record "
        "for cleanup from DNS cache), delete (delete from LDAP). Default: query"
    )
    recordopts.add_argument(
        "-t",
        "--type",
        choices=['A'],
        default='A',
        help="Record type to add (Currently only A records supported)")
    recordopts.add_argument("-d",
                            "--data",
                            metavar='RECORDDATA',
                            help="Record data (IP address)")
    recordopts.add_argument("--allow-multiple",
                            action='store_true',
                            help="Allow multiple A records for the same name")
    recordopts.add_argument("--ttl",
                            type=int,
                            default=180,
                            help="TTL for record (default: 180)")

    args = parser.parse_args()
    #Prompt for password if not set
    authentication = None
    if args.user is not None:
        authentication = NTLM
        if not '\\' in args.user:
            print_f('Username must include a domain, use: DOMAIN\\username')
            sys.exit(1)
        if args.password is None:
            args.password = getpass.getpass()

    # define the server and the connection
    s = Server(args.host, get_info=ALL)
    print_m('Connecting to host...')
    c = Connection(s,
                   user=args.user,
                   password=args.password,
                   authentication=authentication)
    print_m('Binding to host')
    # perform the Bind operation
    if not c.bind():
        print_f('Could not bind with specified credentials')
        print_f(c.result)
        sys.exit(1)
    print_o('Bind OK')
    domainroot = s.info.other['defaultNamingContext'][0]
    forestroot = s.info.other['rootDomainNamingContext'][0]
    if args.forest:
        dnsroot = 'CN=MicrosoftDNS,DC=ForestDnsZones,%s' % forestroot
    else:
        if args.legacy:
            dnsroot = 'CN=MicrosoftDNS,CN=System,%s' % domainroot
        else:
            dnsroot = 'CN=MicrosoftDNS,DC=DomainDnsZones,%s' % domainroot

    if args.print_zones:
        zones = get_dns_zones(c, dnsroot)
        if len(zones) > 0:
            print_m('Found %d domain DNS zones:' % len(zones))
            for zone in zones:
                print('    %s' % zone)
        forestdns = 'CN=MicrosoftDNS,DC=ForestDnsZones,%s' % s.info.other[
            'rootDomainNamingContext'][0]
        zones = get_dns_zones(c, forestdns)
        if len(zones) > 0:
            print_m('Found %d forest DNS zones:' % len(zones))
            for zone in zones:
                print('    %s' % zone)
        return

    target = args.record
    if args.zone:
        zone = args.zone
    else:
        # Default to current domain
        zone = ldap2domain(domainroot)

    if not target:
        print_f('You need to specify a target record')
        return

    if target.lower().endswith(zone.lower()):
        target = target[:-(len(zone) + 1)]

    searchtarget = 'DC=%s,%s' % (zone, dnsroot)
    # print s.info.naming_contexts
    c.search(searchtarget,
             '(&(objectClass=dnsNode)(name=%s))' %
             ldap3.utils.conv.escape_filter_chars(target),
             attributes=['dnsRecord', 'dNSTombstoned', 'name'])
    targetentry = None
    for entry in c.response:
        if entry['type'] != 'searchResEntry':
            continue
        targetentry = entry

    # Check if we have the required data
    if args.action in ['add', 'modify', 'remove'] and not args.data:
        print_f(
            'This operation requires you to specify record data with --data')
        return

    # Check if we need the target record to exists, and if yes if it does
    if args.action in ['modify', 'remove', 'ldapdelete', 'resurrect', 'query'
                       ] and not targetentry:
        print_f('Target record not found!')
        return

    if args.action == 'query':
        print_o('Found record %s' % targetentry['attributes']['name'])
        for record in targetentry['raw_attributes']['dnsRecord']:
            dr = DNS_RECORD(record)
            # dr.dump()
            print(targetentry['dn'])
            print_record(dr, targetentry['attributes']['dNSTombstoned'])
            continue
    elif args.action == 'add':
        # Only A records for now
        addtype = 1
        # Entry exists
        if targetentry:
            if not args.allow_multiple:
                for record in targetentry['raw_attributes']['dnsRecord']:
                    dr = DNS_RECORD(record)
                    if dr['Type'] == 1:
                        address = DNS_RPC_RECORD_A(dr['Data'])
                        print_f(
                            'Record already exists and points to %s. Use --action modify to overwrite or --allow-multiple to override this'
                            % address.formatCanonical())
                        return False
            # If we are here, no A records exists yet
            record = new_record(addtype,
                                get_next_serial(args.host, zone, args.tcp))
            record['Data'] = DNS_RPC_RECORD_A()
            record['Data'].fromCanonical(args.data)
            print_m('Adding extra record')
            c.modify(targetentry['dn'],
                     {'dnsRecord': [(MODIFY_ADD, record.getData())]})
            print_operation_result(c.result)
        else:
            node_data = {
                # Schema is in the root domain (take if from schemaNamingContext to be sure)
                'objectCategory':
                'CN=Dns-Node,%s' % s.info.other['schemaNamingContext'][0],
                'dNSTombstoned':
                False,
                'name':
                target
            }
            record = new_record(addtype,
                                get_next_serial(args.host, zone, args.tcp))
            record['Data'] = DNS_RPC_RECORD_A()
            record['Data'].fromCanonical(args.data)
            record_dn = 'DC=%s,%s' % (target, searchtarget)
            node_data['dnsRecord'] = [record.getData()]
            print_m('Adding new record')
            c.add(record_dn, ['top', 'dnsNode'], node_data)
            print_operation_result(c.result)
    elif args.action == 'modify':
        # Only A records for now
        addtype = 1
        # We already know the entry exists
        targetrecord = None
        records = []
        for record in targetentry['raw_attributes']['dnsRecord']:
            dr = DNS_RECORD(record)
            if dr['Type'] == 1:
                targetrecord = dr
            else:
                records.append(record)
        if not targetrecord:
            print_f('No A record exists yet. Use --action add to add it')
        targetrecord['Serial'] = get_next_serial(args.host, zone, args.tcp)
        targetrecord['Data'] = DNS_RPC_RECORD_A()
        targetrecord['Data'].fromCanonical(args.data)
        records.append(targetrecord.getData())
        print_m('Modifying record')
        c.modify(targetentry['dn'], {'dnsRecord': [(MODIFY_REPLACE, records)]})
        print_operation_result(c.result)
    elif args.action == 'remove':
        addtype = 0
        if len(targetentry['raw_attributes']['dnsRecord']) > 1:
            print_m('Target has multiple records, removing the one specified')
            targetrecord = None
            for record in targetentry['raw_attributes']['dnsRecord']:
                dr = DNS_RECORD(record)
                if dr['Type'] == 1:
                    tr = DNS_RPC_RECORD_A(dr['Data'])
                    if tr.formatCanonical() == args.data:
                        targetrecord = record
            if not targetrecord:
                print_f('Could not find a record with the specified data')
                return
            c.modify(targetentry['dn'],
                     {'dnsRecord': [(MODIFY_DELETE, targetrecord)]})
            print_operation_result(c.result)
        else:
            print_m('Target has only one record, tombstoning it')
            diff = datetime.datetime.today() - datetime.datetime(1601, 1, 1)
            tstime = int(diff.total_seconds() * 10000)
            # Add a null record
            record = new_record(addtype,
                                get_next_serial(args.host, zone, args.tcp))
            record['Data'] = DNS_RPC_RECORD_TS()
            record['Data']['entombedTime'] = tstime
            c.modify(
                targetentry['dn'], {
                    'dnsRecord': [(MODIFY_REPLACE, [record.getData()])],
                    'dNSTombstoned': [(MODIFY_REPLACE, True)]
                })
            print_operation_result(c.result)
    elif args.action == 'ldapdelete':
        print_m('Deleting record over LDAP')
        c.delete(targetentry['dn'])
        print_operation_result(c.result)
    elif args.action == 'resurrect':
        addtype = 0
        if len(targetentry['raw_attributes']['dnsRecord']) > 1:
            print_m(
                'Target has multiple records, I dont  know how to handle this.'
            )
            return
        else:
            print_m('Target has only one record, resurrecting it')
            diff = datetime.datetime.today() - datetime.datetime(1601, 1, 1)
            tstime = int(diff.total_seconds() * 10000)
            # Add a null record
            record = new_record(addtype,
                                get_next_serial(args.host, zone, args.tcp))
            record['Data'] = DNS_RPC_RECORD_TS()
            record['Data']['entombedTime'] = tstime
            c.modify(
                targetentry['dn'], {
                    'dnsRecord': [(MODIFY_REPLACE, [record.getData()])],
                    'dNSTombstoned': [(MODIFY_REPLACE, False)]
                })
            print_o(
                'Record resurrected. You will need to (re)add the record with the IP address.'
            )
Exemple #16
0
    class __Ldap:
        ldap = None

        def __init__(self, settings=None):
            self.settings = settings or Settings()
            self.logger = Logger(__name__)

        def getInstance(self):
            settings = self.settings.getSettings()
            user = settings['ldap']['admin_ldap_username']
            password = settings['ldap']['admin_ldap_password']
            host = self.settings.getServiceIp('ldap')
            self.logger.debug("Connecting to " + host + " with user " + user)
            self.dn_base = settings['ldap']['ldap_cn_base']
            server = Server(host, get_info=ALL)
            self.ldapClient = Connection(server, user=user, password=password, raise_exceptions=True)
            try:
                self.ldapClient.bind()
            except ldap.LDAPSocketOpenError as e:
                self.logger.error("Could not connect to LDAP - SocketOpenError: " + str(e))
            return self

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            return self.ldapClient.unbind()

        def disconnect(self):
            self.ldapClient.unbind()

        def createUser(self, user, firstname, lastname, password, email):
            password = password.encode('utf-8')
            hashPassword = hashlib.md5()
            hashPassword.update(password)
            password = base64.b64encode(hashPassword.digest())
            dn = "cn=" + user + "," + self.dn_base
            attrs = {}
            attrs['objectClass'] = ['inetOrgPerson', 'person', 'top', 'organizationalPerson']
            attrs['cn'] = user
            attrs['userPassword'] = "******" + password.decode('utf-8')
            attrs['sn'] = lastname
            attrs['givenName'] = firstname
            attrs['mail'] = email
            try:
                self.ldapClient.add(dn, attributes=attrs)
                self.logger.info(self.ldapClient.result)
            except ldap.LDAPEntryAlreadyExistsResult:
                self.logger.error("Could not create user, duplicated " + user)
                return False
            except Exception as e:
                self.logger.error("Could not create user: "******"cn="+user+","+self.dn_base
            self.ldapClient.delete(deleteDN)
            return True

        def findUser(self, user):
            self.ldapClient.search(search_base=self.dn_base,
                                   search_filter='(&(objectClass=inetOrgPerson)(cn=' + user + '))',
                                   search_scope=SUBTREE,
                                   attributes=['cn'])

            usernames = []
            for result in self.ldapClient.response:
                cn = result['attributes']['cn'][0]
                if cn:
                    usernames.append(cn)

            return usernames
Exemple #17
0
class LibLDAP(object):
    base_dn = "ou=People,dc=gonzalonazareno,dc=org"
    group_dn = "ou=Group,dc=gonzalonazareno,dc=org"
    conn = ""
    isbind = False

    grupos = {
        'asir1': '1º ASIR',
        'asir2': '2º ASIR',
        'smr1': '1º SMR',
        'smr2': '2º SMR',
        'antiguosalumnos': 'Otros',
        'profesores': 'Profesor',
        'antiguosprofesores': 'A.P.',
        'openstackusers': 'OpenStack',
        'tituladosasir': 'Titulados ASIR',
        'tituladossmr': 'Titulados SMR',
    }

    def __init__(self, username="", password=""):
        try:
            server = Server('ldap.gonzalonazareno.org')
        except:
            print("Error al conectar al servidor")
        try:
            if username != "":
                username = "******" % username
                self.conn = Connection(server,
                                       username,
                                       password,
                                       auto_bind=True)
            else:
                self.conn = Connection(server, auto_bind=True)
            self.isbind = True
        except:
            print("Error al realizar la conexión")

    def buscar(self, filter, attr=[], base_dn=base_dn):
        self.conn.search(base_dn,
                         filter,
                         search_scope=SUBTREE,
                         attributes=attr)
        return [e.entry_attributes_as_dict for e in self.conn.entries]

    def memberOfGroup(self, uid, key=False):
        lista = []
        usuario = "uid=%s,%s" % (uid, self.base_dn)
        grupos = self.buscar("(cn=*)", ['cn', 'member'], self.group_dn)
        for grupo in grupos:

            if usuario in grupo["member"]:
                if key:
                    lista.append(grupo["cn"][0])
                else:
                    lista.append(self.grupos[grupo["cn"][0]])
        return lista

    def isMemberOfGroup(self, uid, grupo):
        return grupo in self.memberOfGroup(uid, key=True)

    def isMemberOfGroups(self, uid, grupos=[]):
        for grupo in grupos:
            if grupo in self.memberOfGroup(uid, key=True):
                return True
        return False

    def conv_filtro(self, filtro):
        cadena = ""
        if len(filtro) > 0:
            cadena = "(&(objectClass=inetOrgPerson)"
            for campo, valor in filtro.items():
                if campo == "grupo" and valor == 'all':
                    cadena += "(uid=*)"
                elif campo == "grupo":
                    grupos = [valor]
                    if valor == "alumnos":
                        grupos = [
                            "asir1", "asir2", "smr1", "smr2", "antiguosalumnos"
                        ]
                    elif valor == "soloalumnos":
                        grupos = ["asir1", "asir2", "smr1", "smr2"]
                    elif valor == "allprofesores":
                        grupos = ["profesores", "antiguosprofesores"]
                    elif valor == "alltitulados":
                        grupos = ["tituladosasir", "tituladossmr"]
                    cadena2 = "(|"
                    for grupos in grupos:
                        cadena2 += "(memberOf=cn=%s,ou=Group,dc=gonzalonazareno,dc=org)" % grupos
                    cadena2 += ")"
                    cadena += cadena2
                else:
                    cadena += "(%s=%s*)" % (campo, valor)
            cadena += ")"
        return cadena

    def logout(self):
        self.conn.unbind()
        self.isbind = False

    def add(self, uid, datos):
        usuario = "uid=%s,%s" % (uid, self.base_dn)
        self.conn.add(usuario, attributes=datos)

    def delete(self, uid):
        usuario = "uid=%s,%s" % (uid, self.base_dn)
        self.conn.delete(usuario)

    def modify(self, uid, datos):
        usuario = "uid=%s,%s" % (uid, self.base_dn)
        for c, v in datos.items():
            datos[c] = [(MODIFY_REPLACE, [v])]
        self.conn.modify(usuario, datos)

    def modUserGroup(self, uid, grupo, adddel):
        modlist = []
        usuario = "uid=%s,%s" % (uid, self.base_dn)
        grupo = "cn=%s,%s" % (grupo, self.group_dn)
        if adddel == "add":
            return self.conn.modify(grupo,
                                    {'member': [(MODIFY_ADD, [usuario])]})
        elif adddel == "del":
            return self.conn.modify(grupo,
                                    {'member': [(MODIFY_DELETE, [usuario])]})
Exemple #18
0
class AD(object):
    '''AD域的操作
    '''
    def __init__(self):
        '''初始化加载日志配置
        AD域连接
        AD基础信息加载
        '''
        # 初始化加载日志配置
        self.__setup_logging(path=LOG_CONF)
        SERVER = Server(
            host=LDAP_IP,
            port=636,  # 636安全端口
            use_ssl=True,
            get_info=ALL,
            connect_timeout=5)  # 连接超时为5秒
        try:
            self.conn = Connection(
                server=SERVER,
                user=USER,
                password=PASSWORD,
                auto_bind=True,
                read_only=False,  # 禁止修改数据True
                receive_timeout=10)  # 10秒内没返回消息则触发超时异常
            logging.info("distinguishedName:%s res: %s" %
                         (USER, self.conn.bind()))
        except BaseException as e:
            logging.error("AD域连接失败,请检查IP/账户/密码")
        finally:
            self.conn.closed

    def __setup_logging(self,
                        path=LOG_CONF,
                        default_level=logging.INFO,
                        env_key="LOG_CFG"):
        value = os.getenv(env_key, None)
        if value:
            path = value
        if os.path.exists(path):
            with open(path, "r") as f:
                config = yaml.safe_load(f)
                logging.config.dictConfig(config)
        else:
            logging.basicConfig(level=default_level)

    def get_users(self, attr=ALL_ATTRIBUTES):
        '''
        @param {type}
        @return: total_entries 此次查询到的记录数目
        @msg: 获取所有用户
        '''
        entry_list = self.conn.extend.standard.paged_search(
            search_base=ENABLED_BASE_DN,
            search_filter=USER_SEARCH_FILTER,
            search_scope=SUBTREE,
            attributes=attr,
            paged_size=5,
            generator=False)  # 关闭生成器,结果为列表
        total_entries = 0
        for entry in entry_list:
            total_entries += 1
        logging.info("共查询到记录条目: " + str(total_entries))
        return entry_list

    def get_ous(self, attr=None):
        '''
        @param {type}
        @return: res所有OU
        @msg: 获取所有OU
        '''
        self.conn.search(search_base=ENABLED_BASE_DN,
                         search_filter=OU_SEARCH_FILTER,
                         attributes=attr)
        result = self.conn.response_to_json()
        res_list = json.loads(result)['entries']
        return res_list[::-1]

    def get_level_users(self, SEARCH_BASE, attr=ALL_ATTRIBUTES):
        '''
        @param {type}
        @return: total_entries 此次查询到的记录数目
        @msg: 获取某OU下用户
        '''
        entry_list = self.conn.extend.standard.paged_search(
            search_base=SEARCH_BASE,
            search_filter=USER_SEARCH_FILTER,
            search_scope=LEVEL,
            attributes=attr,
            paged_size=5,
            generator=False)  # 关闭生成器,结果为列表
        total_entries = 0
        for entry in entry_list:
            total_entries += 1
        logging.info("共查询到记录条目: " + str(total_entries))
        return entry_list

    def handle_excel(self, path):
        '''
        @param path{string} excel文件绝对路径
        @return: result: { 'page_flag': True, 'person_list': [[], [], ...] }
        @msg: 表格文件预处理
        1.增加行列数判————行数决定AD的查询是否分页,列数用以判断必须列数据完整性与补充列;
        2.判断必须列【工号|姓名|部门】是否存在且是否有空值
        3.人员列表的使用sort函数排序key用lambda函数,排序条件(i[2].count('.'), i[2], i[0])为(部门层级、部门名称、工号)
        '''
        try:
            # 1.开始源文件格式扫描
            df = pd.read_excel(path)  # 读取源文件
            a, b = df.shape  # 表格行列数
            cols = df.columns.tolist()  # 表格列名列表
            is_ex_null = df.isnull().any().tolist()  # 列是否存在空值
            dic = dict(zip(cols, is_ex_null))  # 存在空值的列
            if int("工号" in cols) + int("姓名" in cols) + int(
                    "部门" in cols) < 3:  # 判断必须列是否都存在
                logging.error(
                    "表格缺少必要列【工号|姓名|部门】请选择正确的源文件;或者将相应列列名修改为【工号|姓名|部门】")
                exit()
            elif int(dic["工号"]) + int(dic["姓名"]) + int(
                    dic["部门"]) > 0:  # 判断必须列是否有空值
                logging.error("必要列存在空值记录,请检查补全后重试:" + '\n' +
                              str(df[df.isnull().values == True]))
            else:
                df = pd.read_excel(path, usecols=[i for i in range(0, b)])
                use_cols = ["工号", "姓名", "部门"]  # 使用的必须列
                for c in ["邮件", "电话", "岗位"]:  # 扩展列的列名在这里添加即可
                    if c in cols:
                        use_cols.append(c)
                df = df[use_cols]  # 调整df使用列顺序
                person_list = df.values.tolist()  # df数据框转list
                person_list.sort(key=lambda i: (i[2].count('.'), i[2], i[0]),
                                 reverse=False)  # 多条件排序
                # 2.开始处理列表
                for i, row in enumerate(person_list):
                    job_id, name, depart = row[0:3]
                    # 将部门列替换成DN
                    row[2] = 'CN=' + str(
                        name + str(job_id)) + ',' + 'OU=' + ',OU='.join(
                            row[2].split('.')[::-1]) + ',' + ENABLED_BASE_DN
                    row.append(CUSTOM_SAMA + str(job_id).zfill(6)
                               )  # 增加登录名列,对应AD域user的 sAMAccountname 属性
                    row.append(name + str(job_id))  # 增加CN列,对应user的 cn 属性
                # 3.开始处理返回字典
                result_dic = dict()  # 返回字典
                if a > 1000:
                    result_dic['page_flag'] = True
                else:
                    result_dic['page_flag'] = False
                result_dic['person_list'] = person_list
                return result_dic
        except Exception as e:
            logging.error(e)
            return None

    def generate_pwd(self, count):
        '''
        @param count{int} 所需密码长度
        @return: pwd: 生成的随机密码
        @msg: 生成随机密码,必有数字、大小写、特殊字符且数目伪均等;
        '''
        pwd_list = []
        a, b = count // 4, count % 4
        # 四种类别先均分除数个字符
        pwd_list.extend(random.sample(string.digits, a))
        pwd_list.extend(random.sample(string.ascii_lowercase, a))
        pwd_list.extend(random.sample(string.ascii_uppercase, a))
        pwd_list.extend(random.sample('!@#$%^&*()', a))
        # 从四种类别中再取余数个字符
        pwd_list.extend(
            random.sample(
                string.digits + string.ascii_lowercase +
                string.ascii_uppercase + '!@#$%^&*()', b))
        random.shuffle(pwd_list)
        pwd_str = ''.join(pwd_list)
        return pwd_str

    def write2txt(self, path, content):
        '''
        @param path{string} 写入文件路径;content{string} 每行写入内容
        @return:
        @msg: 每行写入文件
        '''
        try:
            if os.path.exists(path):
                with open(path, mode='a', encoding='utf-8') as file:
                    file.write(content + '\n')
            else:
                with open(path, mode='a', encoding='utf-8') as file:
                    file.write(content + '\n')
            return True
        except Exception as e:
            logging.error(e)
            return False

    def del_ou_right(self, flag):
        '''
        @param cmd_l{list} 待执行的powershell命令列表
        @return: True/False
        @msg: 连接远程windows并批量执行powershell命令
        '''
        # powershell命令 用于启用/关闭OU 防止对象被意外删除 属性
        # 防止对象被意外删除×
        enable_del = [
            "Import-Module ActiveDirectory",
            "Get-ADOrganizationalUnit -filter * -Properties ProtectedFromAccidentalDeletion | where {"
            "$_.ProtectedFromAccidentalDeletion -eq $true} |Set-ADOrganizationalUnit "
            "-ProtectedFromAccidentalDeletion $false"
        ]
        # 防止对象被意外删除√
        disable_del = [
            "Import-Module ActiveDirectory",
            "Get-ADOrganizationalUnit -filter * -Properties ProtectedFromAccidentalDeletion | where {"
            "$_.ProtectedFromAccidentalDeletion -eq $false} |Set-ADOrganizationalUnit "
            "-ProtectedFromAccidentalDeletion $true"
        ]
        flag_map = {0: enable_del, 1: disable_del}

        try:
            win = winrm.Session('http://' + LDAP_IP + ':5985/wsman',
                                auth=(WINRM_USER, WINRM_PWD))
            for cmd in flag_map[flag]:
                ret = win.run_ps(cmd)
            if ret.status_code == 0:  # 调用成功 减少日志写入
                # if flag == 0:
                #     logging.info("防止对象被意外删除×")
                # elif flag == 1:
                #     logging.info("防止对象被意外删除√")
                return True
            else:
                return False
        except Exception as e:
            logging.error(e)
            return False

    def create_obj(self, dn=None, type='user', info=None):
        '''
        @param dn{string}, type{string}'user'/'ou'
        @return: res新建结果, self.conn.result修改结果
        @msg:新增对象
        '''
        object_class = {
            'user': ['user', 'posixGroup', 'top'],
            'ou': ['organizationalUnit', 'posixGroup', 'top'],
        }
        if info is not None:
            [job_id, name, dn, email, tel, title, sam, cn] = info
            user_attr = {
                'sAMAccountname': sam,  # 登录名
                'userAccountControl': 544,  # 启用账户
                'title': title,  # 头衔
                'givenName': name[0:1],  # 姓
                'sn': name[1:],  # 名
                'displayname': name,  # 姓名
                'mail': email,  # 邮箱
                'telephoneNumber': tel,  # 电话号
            }
        else:
            user_attr = None
        # 创建之前需要对dn中的OU部分进行判断,如果没有需要创建
        dn_base = dn.split(',', 1)[1]
        check_ou_res = self.check_ou(dn_base)
        if not check_ou_res:
            logging.error('check_ou失败,未知原因!')
            return False
        else:
            self.conn.add(dn=dn,
                          object_class=object_class[type],
                          attributes=user_attr)
            add_result = self.conn.result

            if add_result['result'] == 0:
                logging.info('新增对象【' + dn + '】成功!')
                if type == 'user':  # 若是新增用户对象,则需要一些初始化操作
                    self.conn.modify(
                        dn, {'userAccountControl': [('MODIFY_REPLACE', 512)]}
                    )  # 激活用户                                                               # 如果是用户时
                    new_pwd = self.generate_pwd(8)
                    old_pwd = ''
                    self.conn.extend.microsoft.modify_password(
                        dn, new_pwd, old_pwd)  # 初始化密码
                    info = 'SAM: ' + sam + ' PWD: ' + new_pwd + ' DN: ' + dn
                    save_res = self.write2txt(PWD_PATH, info)  # 将账户密码写入文件中
                    if save_res:
                        logging.info('保存初始化账号密码成功!')
                    else:
                        logging.error('保存初始化账号密码失败: ' + info)
                    self.conn.modify(dn, {'pwdLastSet':
                                          (2, [0])})  # 设置第一次登录必须修改密码
            elif add_result['result'] == 68:
                logging.error('entryAlreadyExists 用户已经存在')
            elif add_result['result'] == 32:
                logging.error('noSuchObject 对象不存在ou错误')
            else:
                logging.error('新增对象: ' + dn + ' 失败!其他未知错误')
            return add_result

    def del_obj(self, dn, type):
        '''
        @param dn{string}
        @return: res修改结果
        @msg: 删除对象
        '''
        if type == 'ou':
            self.del_ou_right(flag=0)
            res = self.conn.delete(dn=dn)
            self.del_ou_right(flag=1)
        else:
            res = self.conn.delete(dn=dn)
        if res == True:
            logging.info('删除对象' + dn + '成功!')
            return res
        else:
            return False

    def update_obj(self, old_dn, info=None):
        '''
        @param {type}
        @return:
        @msg: 更新对象
        '''
        if info is not None:
            [job_id, name, dn, email, tel, title, sam, cn] = info
            # 组成更新属性之前需要对dn中的OU部分进行判断,如果没有需要创建
            dn_base = dn.split(',', 1)[1]
            check_ou_res = self.check_ou(dn_base)
            if not check_ou_res:
                logging.error('check_ou失败,未知原因!')
                return False
            else:
                attr = {
                    'distinguishedName': dn,  # dn
                    'sAMAccountname': sam,  # 登录名
                    'title': title,  # 头衔
                    'givenName': name[0:1],  # 姓
                    'sn': name[1:],  # 名
                    'displayname': name,  # 姓名
                    'mail': email,  # 邮箱
                    'telephoneNumber': tel,  # 电话号
                }
        else:
            attr = None
        changes_dic = {}
        for k, v in attr.items():
            if not self.conn.compare(dn=old_dn, attribute=k, value=v):  # 待修改属性
                if k == "distinguishedName":  # 若属性有distinguishedName则需要移动user或ou
                    # 若dn修改了需要将密码文件这个人的dn信息更新下
                    self.update_pwd_file_line(old_dn=old_dn, new_dn=dn)
                    self.move_obj(dn=old_dn, new_dn=v)
                changes_dic.update({k: [(MODIFY_REPLACE, [v])]})
        if len(changes_dic) != 0:  # 有修改的属性时
            modify_res = self.conn.modify(dn=dn, changes=changes_dic)
            logging.info('更新对象: ' + dn + ' 更新内容: ' + str(changes_dic))
        return self.conn.result

    def rename_obj(self, dn, newname):
        '''
        @param newname{type}新的名字,User格式:"cn=新名字";OU格式:"OU=新名字"
        @return: 修改结果
        @msg: 重命名对象
        '''
        res = self.conn.modify_dn(dn, newname)
        if res == True:
            return True
        else:
            return False

    def move_obj(self, dn, new_dn):
        '''
        @param {type}
        @return:
        @msg: 移动对象到新OU
        '''
        relative_dn, superou = new_dn.split(",", 1)
        res = self.conn.modify_dn(dn=dn,
                                  relative_dn=relative_dn,
                                  new_superior=superou)
        if res == True:
            return True
        else:
            return False

    def compare_attr(self, dn, attr, value):
        '''
        @param {type}
        @return:
        @msg:比较员工指定的某个属性
        '''
        res = self.conn.compare(dn=dn, attribute=attr, value=value)
        return res

    def check_ou(self, ou, ou_list=None):
        '''
        @param {type}
        @return:
        @msg: 递归函数
    如何判断OU是修改了名字而不是新建的:当一个OU里面没有人就判断此OU被修改了名字,删除此OU;
    不管是新建还是修改了名字,都会将人员转移到新的OU下面:需要新建OU则创建OU后再添加/转移人员
    check_ou的作用是为人员的变动准备好OU
        '''
        if ou_list is None:
            ou_list = []
        self.conn.search(ou, OU_SEARCH_FILTER)  # 判断OU存在性

        while self.conn.result['result'] == 0:
            if ou_list:
                for ou in ou_list[::-1]:
                    self.conn.add(ou, 'organizationalUnit')
            return True
        else:
            ou_list.append(ou)
            ou = ",".join(ou.split(",")[1:])
            self.check_ou(ou, ou_list)  # 递归判断
            return True

    def scan_ou(self):
        '''扫描的时候,必须保证此OU为叶子节点,否则报notAllowedOnNonLeaf错误,
        例如此次空OU——OU=开发部,OU=核心技术部,OU=RAN,OU=上海总部,DC=randolph,DC=com
        的倒数第一、二层都是空OU,但是必须得先删除倒数第一层
        因此在获取所有OU列表的位置get_ous就将获得的结果倒叙(用切片[::-1])
        '''
        res = self.get_ous(attr=['distinguishedName'])
        # 调用ps脚本,防止对象被意外删除×
        modify_right_res = self.del_ou_right(flag=0)
        for i, ou in enumerate(res):
            dn = ou['attributes']['distinguishedName']
            # 判断dd下面是否有用户,没有用户的直接删除
            self.conn.search(search_base=dn, search_filter=USER_SEARCH_FILTER)
            if not self.conn.entries:  # 没有用户存在的空OU,可以进行清理
                try:
                    delete_res = self.conn.delete(dn=dn)
                    if delete_res:
                        logging.info('删除空的OU: ' + dn + ' 成功!')
                    else:
                        logging.error('删除操作处理结果' + str(self.conn.result))
                except Exception as e:
                    logging.error(e)
        else:
            logging.info("没有空OU,OU扫描完成!")
        # 防止对象被意外删除√
        self.del_ou_right(flag=1)

    def disable_users(self, path):
        '''
        @param {type}
        @return:
        @msg: 将AD域内的用户不在csv表格中的定义为离职员工
        '''
        result = ad.handle_excel(path)
        newest_list = []  # 全量员工列表
        for person in result['person_list']:
            job_id, name, dn, email, tel, title, sam, cn = person[0:8]
            dd = str(dn).split(',', 1)[1]
            newest_list.append(name)
        # 查询AD域现有员工
        res = self.get_users(attr=[
            'distinguishedName', 'name', 'cn', 'displayName',
            'userAccountControl'
        ])
        for i, ou in enumerate(res):
            ad_user_distinguishedName, ad_user_displayName, ad_user_cn, ad_user_userAccountControl = ou[
                'attributes']['distinguishedName'], ou['attributes'][
                    'displayName'], ou['attributes']['cn'], ou['attributes'][
                        'userAccountControl']
            rela_dn = "cn=" + str(ad_user_cn)
            # 判断用户不在最新的员工表格中 或者 AD域中某用户为禁用用户
            if ad_user_displayName not in newest_list or ad_user_userAccountControl in DISABLED_USER_FLAG:
                try:
                    # 禁用用户
                    self.conn.modify(
                        dn=ad_user_distinguishedName,
                        changes={'userAccountControl': (2, [546])})
                    logging.info("在AD域中发现不在表格中用户,禁用用户:" +
                                 ad_user_distinguishedName)
                    # 移动到离职组 判断OU存在性
                    self.conn.search(DISABLED_BASE_DN,
                                     OU_SEARCH_FILTER)  # 判断OU存在性
                    if self.conn.entries == []:  # 搜不到离职员工OU则需要创建此OU
                        self.create_obj(dn=DISABLED_BASE_DN, type='ou')
                    # 移动到离职组
                    self.conn.modify_dn(dn=ad_user_distinguishedName,
                                        relative_dn=rela_dn,
                                        new_superior=DISABLED_BASE_DN)
                    logging.info('将禁用用户【' + ad_user_distinguishedName +
                                 '】转移到【' + DISABLED_BASE_DN + '】')
                except Exception as e:
                    logging.error(e)

    def create_user_by_excel(self, path):
        '''
        @param path{string} 用于新增用户的表格
        @return:
        @msg:
        '''
        res_dic = self.handle_excel(path)
        for person in res_dic['person_list']:
            user_info = person
            self.create_obj(info=user_info)

    def ad_update(self, path):
        '''AD域的初始化/更新——从表格文件元数据更新AD域:
        判断用户是否在AD域中——不在则新增;
        在则判断该用户各属性是否与表格中相同,有不同则修改;
        完全相同的用户不用作处理;
        '''
        # 准备表格文件
        result = ad.handle_excel(path)
        ori_data = result['person_list']
        try:
            self.del_ou_right(flag=0)  # 防止对象被意外删除×
            with tqdm(iterable=ori_data,
                      ncols=100,
                      total=len(ori_data),
                      desc='处理进度',
                      unit='人') as tqdm_ori_data:  # 封装进度条
                for person in tqdm_ori_data:
                    dn, cn = person[2], person[7]
                    user_info = person
                    dd = str(dn).split(',', 1)[1]
                    # 根据cn判断用户是否已经存在
                    filter_phrase_by_cn = "(&(objectclass=person)(cn=" + cn + "))"
                    search_by_cn = self.conn.search(
                        search_base=ENABLED_BASE_DN,
                        search_filter=filter_phrase_by_cn,
                        attributes=['distinguishedName'])
                    search_by_cn_json_list = json.loads(
                        self.conn.response_to_json())['entries']
                    search_by_cn_res = self.conn.result
                    if search_by_cn == False:  # 根据cn搜索失败,查无此人则新增
                        self.create_obj(info=user_info)
                    else:
                        old_dn = search_by_cn_json_list[0][
                            'dn']  # 部门改变的用户的现有部门,从表格拼接出来的是新的dn在user_info中带过去修改
                        self.update_obj(old_dn=old_dn, info=user_info)
                    # break                     # 可测试一个例子
                self.del_ou_right(flag=1)  # 防止对象被意外删除√
        except KeyboardInterrupt:
            tqdm_ori_data.close()
            raise
        tqdm_ori_data.close()

    def handle_pwd_expire(self, attr=None):
        '''
        @param {type}
        @return:
        @msg: 处理密码过期 设置密码不过期 需要补全理论和测试
        参考理论地址:
        https://stackoverflow.com/questions/18615958/ldap-pwdlastset-unable-to-change-without-error-showing
        '''
        attr = ['pwdLastSet']
        self.conn.search(search_base=ENABLED_BASE_DN,
                         search_filter=USER_SEARCH_FILTER,
                         attributes=attr)
        result = self.conn.response_to_json()
        res_list = json.loads(result)['entries']
        for l in res_list:
            pwdLastSet, dn = l['attributes']['pwdLastSet'], l['dn']
            modify_res = self.conn.modify(dn,
                                          {'pwdLastSet':
                                           (2, [-1])})  # pwdLastSet只能给-1 或 0
            if modify_res:
                logging.info('密码不过期-修改用户: ' + dn)

    def update_pwd_file_line(self, old_dn=None, new_dn=None, new_pwd=None):
        '''
        @param dn{string}
        @return: 修改结果
        @msg: 当用户的dn或密码被程序更新,将会在这里更新对应部分的信息
        采用临时文件替换源文件的方式,节省内存,但占硬盘
        参考文章: https://www.cnblogs.com/wuzhengzheng/p/9692368.html
        '''
        with open(PWD_PATH, mode='rt', encoding='utf-8') as file, \
                open('TEMP.txt', mode='wt', encoding='utf-8') as temp_file:
            for line in file:
                if old_dn and new_dn:  # dn被修改
                    if old_dn in line:
                        line = line.replace(old_dn, new_dn)
                        temp_file.write(line)
                    else:
                        temp_file.write(line)
                elif new_pwd and old_dn:  # 密码被修改
                    if old_dn in line:
                        # 需要正则匹配旧的密码
                        pattern = "PWD: (.+?)\\n"  # 惰性匹配
                        local = re.findall(pattern, line)
                        old_pwd = local[0]
                        line = line.replace(old_pwd, new_pwd)
                        temp_file.write(line)
                    else:
                        temp_file.write(line)
        os.remove(PWD_PATH)
        os.rename('TEMP.txt', PWD_PATH)

    def modify_pwd(self, cn):
        '''
        @param cn{string} 姓名工号 戴东1325
        @return: 修改结果
        @msg: 修改密码
        '''
        # 根据cn判断用户是否已经存在
        filter_phrase_by_cn = "(&(objectclass=person)(cn=" + cn + "))"
        search_by_cn = self.conn.search(search_base=ENABLED_BASE_DN,
                                        search_filter=filter_phrase_by_cn,
                                        attributes=['distinguishedName'])
        search_by_cn_json_list = json.loads(
            self.conn.response_to_json())['entries']
        if search_by_cn:
            new_pwd = self.generate_pwd(8)
            old_pwd = ''
            dn = search_by_cn_json_list[0]['dn']
            modify_password_res = self.conn.extend.microsoft.modify_password(
                dn, new_pwd, old_pwd)
            if modify_password_res:
                logging.info('更新了对象: ' + dn + ' 的密码')
                is_exist = os.path.exists(PWD_PATH)
                if not is_exist:  # 校验密码文件存在性
                    info = 'DN: ' + dn + ' PWD: ' + new_pwd
                    save_res = self.write2txt(PWD_PATH, info)  # 将账户密码写入文件中
                    if save_res:
                        logging.info('保存初始化账号密码成功!')
                    else:
                        logging.error('保存初始化账号密码失败: ' + info)
                else:
                    # 若密码修改了需要将密码文件这个人的密码信息更新下
                    with open(PWD_PATH, mode='rt', encoding='utf-8') as file:
                        if dn in file.read():
                            is_exist_pwd_record = True
                        else:
                            is_exist_pwd_record = False
                    if is_exist_pwd_record:  # 若发现此人信息在密码文件里则更新,否则需创建
                        self.update_pwd_file_line(old_dn=dn, new_pwd=new_pwd)
                    else:
                        info = 'DN: ' + dn + ' PWD: ' + new_pwd  # 因为是修改密码,所以dn未修改
                        self.write2txt(PWD_PATH, info)
            else:
                logging.error('更新对象密码失败!: ' + dn)
        else:
            logging.error('查无此人!请检查待修改密码对象格式是否为【姓名工号】')
Exemple #19
0
    # If clients wants to remove an entry
    elif operation == '3' or operation == 'r':
        to_delete = input("Which user would you like to remove: > ")
        sure = input("Are you sure you want to delete " + to_delete +
                     "? y/n : ")
        y_or_n = False
        # Just a little security to be sure what to delete
        while not y_or_n:
            if (sure == 'y' or sure == 'n'):
                y_or_n = True
            else:
                sure = input("type y or n : > ")
        if sure == 'n':
            pass
        else:
            conn.delete('cn=' + to_delete +
                        ', ou=students, dc=security, dc=ch')
            print(conn.result)

    # If client wants to modify an entry
    elif operation == '4' or operation == 'm':
        to_modify, attr_to_modify, val_to_modify = [], [], []
        str = 'y'
        to_modify.append(input("Which user would you like to modify? > "))
        print(
            "We will get the attributes and values you want to modify, once you are done, type \'n\'."
        )

        while str != 'n':
            attr_to_modify.append(input("Attribute to modify: > "))
            val_to_modify.append(input("Value to modify: > "))
            str = input("Continue? y/n > ")
class LdapOLC(object):
    """A wrapper class to operate on the o=gluu DIT of the LDAP.

    Args:
        hostname (string): hostname of the server running the LDAP server
        addr (string): uri of ldap server, such as ldaps://ldp.foo.org:1636
        binddn (string): bind dn for ldap server
        password (string): the password of binddn
    """
    def __init__(self, addr, binddn, passwd):
        self.addr = addr
        self.binddn = binddn
        self.passwd = passwd
        self.server = None
        self.conn = None
        self.hostname = get_host_port(addr)[0]

    def connect(self):
        """Makes connection to ldap server and returns result
        
        Returns:
            the ldap connection result
        """
        logger.debug("Making Ldap Connection")
        self.server = Server(self.addr, use_ssl=True)
        self.conn = Connection(self.server,
                               user=self.binddn,
                               password=self.passwd)
        return self.conn.bind()

    def loadModules(self, *modules):
        """This function creates ldap entry on server for loading nodules.
        
        Args:
            modules (list): list of modules to be loaded 
        
        Returns:
            -1 if modulas were already loaded, else returns modify result
        """

        #Get loaded modules
        self.conn.search(search_base='cn=module{0},cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcModuleLoad"])

        #addList are modules that will be loaded
        addList = list(modules)

        if self.conn.response:

            #if a module is allread loaded, remove it from addList
            for a in self.conn.response[0]['attributes']['olcModuleLoad']:
                r = re.split("{\d+}", a)
                if len(r) == 1:
                    m = r[0]
                else:
                    m = r[1]
                mn = m.split('.')
                if mn[0] in addList:
                    addList.remove(mn[0])

        #If there is still modules to be loaded, add them and return
        #modify results
        if addList:

            return self.conn.modify('cn=module{0},cn=config',
                                    {'olcModuleLoad': [MODIFY_ADD, addList]})

        #If all modules were loaded previously, return -1
        return -1

    def checkAccesslogDBEntry(self):
        """Checks if access logdb (cn=accesslog) entry exists
        
        Returns:
            search results of cn=accesslog
        """

        return self.conn.search(search_base='cn=config',
                                search_filter='(olcSuffix=cn=accesslog)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def accesslogDBEntry(self,
                         replicator_dn,
                         log_dir="/opt/gluu/data/accesslog"):
        """This function creates ldap entry on server for accesslog database.
        
        Args:
            replicator_dn (string): replicator dn for replication
            log_dir (string, optional): accesslog database directorsy,
                    default to /opt/gluu/data/accesslog
        
        Returns:
            None if accesslogdb entry is already exists else ldap modifcation 
            result for adding accsesslogdb entry.
        """

        attributes = {
            'objectClass': ['olcDatabaseConfig', 'olcMdbConfig'],
            'olcDatabase':
            '{2}mdb',
            'olcDbDirectory':
            log_dir,
            'OlcDbMaxSize':
            1073741824,
            'olcSuffix':
            'cn=accesslog',
            'olcRootDN':
            'cn=admin, cn=accesslog',
            'olcRootPW':
            ldap_encode(self.passwd),
            'olcDbIndex': [
                'default eq',
                'objectClass,entryCSN,entryUUID,reqEnd,reqResult,reqStart,reqDN'
            ],
            'olcLimits':
            'dn.exact="{0}" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited'
            .format(replicator_dn),
        }
        #check if accesslogdb entry is allread exists. If not exists, create it.
        if not self.checkAccesslogDBEntry():
            return self.conn.add('olcDatabase={2}mdb,cn=config',
                                 attributes=attributes)

    def checkSyncprovOverlaysDB1(self):
        """Checks if overlay configuration entry exists on first database
        
        Returns:
            search results of olcOverlay=syncprov
        """
        return self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                                search_filter='(olcOverlay=syncprov)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def syncprovOverlaysDB1(self):
        """This function creates overlay configuration on first database

        Returns:
            None if overlay configuration entry is already exists 
            else ldap modifcation result for adding overlay configuration entry.
        """
        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcSyncProvConfig'],
            'olcOverlay': 'syncprov',
            'olcSpReloadHint': 'TRUE',
            'olcSpCheckPoint': '100 10',
            'olcSpSessionlog': '10000',
        }
        #If not overlay configuration on first database is not exists, crtate it
        if not self.checkSyncprovOverlaysDB1():
            self.conn.add('olcOverlay=syncprov,olcDatabase={1}mdb,cn=config',
                          attributes=attributes)
            if self.conn.result['description'] == 'success':
                return True

    def checkSyncprovOverlaysDB2(self):
        """Checks if overlay configuration entry exists on second database
        
        Returns:
            search results of olcOverlay=syncprov
        """
        return self.conn.search(search_base='olcDatabase={2}mdb,cn=config',
                                search_filter='(olcOverlay=syncprov)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def syncprovOverlaysDB2(self):
        """This function creates overlay configuration on second database

        Returns:
            None if overlay configuration entry is already exists 
            else ldap modifcation result for adding overlay configuration entry.
        """
        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcSyncProvConfig'],
            'olcOverlay': 'syncprov',
            'olcSpNoPresent': 'TRUE',
            'olcSpReloadHint': 'TRUE',
        }
        #If not overlay configuration on second database
        #is not exists, crtate it
        if not self.checkSyncprovOverlaysDB2():
            self.conn.add('olcOverlay=syncprov,olcDatabase={2}mdb,cn=config',
                          attributes=attributes)

            if self.conn.result['description'] == 'success':
                return True

    def checkServerID(self):
        """Checks if Server ID entry exists
        
        Returns:
            search results of olcServerID
        """
        return self.conn.search(search_base='cn=config',
                                search_filter='(objectClass=*)',
                                search_scope=BASE,
                                attributes=["olcServerID"])

    def setServerID(self, sid):
        """This function sets Server ID for replication
        
        Args:
            sid (int): Server ID for this server

        Returns:
            ldap modifcation result for setting server id entry.
        """

        #modification type is add
        mod_type = MODIFY_ADD

        #check if server id exists.
        self.conn.search(search_base='cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcServerID"])

        #If server id exists, modfication type id replace
        if self.checkServerID():
            if self.conn.response[0]['attributes']['olcServerID']:
                mod_type = MODIFY_REPLACE

        return self.conn.modify('cn=config',
                                {'olcServerID': [mod_type, str(sid)]})

    def setDBIndexes(self):
        """This function sets indexes for accesslog database
        
        Returns:
            ldap modifcation result for setting indexes for accesslog.
        """

        #check if indexes exist
        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcDbIndex"])
        addList = ["entryCSN eq", "entryUUID eq"]

        #remove index that is already exist
        if self.conn.response:
            for idx in self.conn.response[0]['attributes']['olcDbIndex']:
                if idx in addList:
                    addList.remove(idx)

        return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                {'olcDbIndex': [MODIFY_ADD, addList]})

    def checkAccesslogPurge(self):
        """This function checks if accesslog purge entry exists
        
        Returns:
            search result of objectClass=olcAccessLogConfig
        """
        return self.conn.search(
            search_base='cn=config',
            search_filter='(objectClass=olcAccessLogConfig)',
            search_scope=SUBTREE,
            attributes=["olcAccessLogPurge"])

    def accesslogPurge(self, purge='0:24:0 1:0:0'):
        """This function creates purge interval and age for accessogdb entries
        
        Args:
            purge (string, optional): interval and age representation separeted
                by a space in the form: "D+H:M:S" 
                where D: day, H: hour, M: min, S:sec

        Returns:
            ldap modifcation result for setting accesslog purge entry.
        """

        #split data to interval and age.
        p, a = purge.split()
        pl = p.split(':')
        al = a.split(':')

        olcAccessLogPurge = ''

        #all entries except day, should be double in length
        if not pl[0] == '0':
            olcAccessLogPurge += pl[0].zfill(2) + '+'
        olcAccessLogPurge += "{}:{}".format(pl[1].zfill(2),
                                            pl[2].zfill(2)) + ' '

        if not al[0] == '0':
            olcAccessLogPurge += al[0].zfill(2) + '+'
        olcAccessLogPurge += "{}:{}".format(al[1].zfill(2), al[2].zfill(2))

        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcAccessLogConfig'],
            'olcOverlay': 'accesslog',
            'olcAccessLogDB': 'cn=accesslog',
            'olcAccessLogOps': 'writes',
            'olcAccessLogSuccess': 'TRUE',
            'olcAccessLogPurge': olcAccessLogPurge,
        }

        if not self.checkAccesslogPurge():
            return self.conn.add(
                'olcOverlay=accesslog,olcDatabase={1}mdb,cn=config',
                attributes=attributes)

    def removeMirrorMode(self):
        """This function removes mirror mode entry

        Returns:
            None if server is not in mirror mode else ldap modification result
        """
        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcMirrorMode"])

        if not self.conn.response:
            return

        if self.conn.response[0]['attributes']['olcMirrorMode']:
            return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                    {"olcMirrorMode": [MODIFY_REPLACE, []]})

    def checkMirroMode(self):
        """This function checks if server is in mirror mode

        Returns:
            False if server is not in mirror mode else search result of 
            olcMirrorMode
            
        """
        r = self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                             search_filter='(objectClass=*)',
                             search_scope=BASE,
                             attributes=["olcMirrorMode"])
        if r:
            if self.conn.response[0]['attributes']:
                if self.conn.response[0]['attributes']['olcMirrorMode']:
                    return self.conn.response[0]['attributes']['olcMirrorMode']

        return False

    def makeMirroMode(self):
        """This function makse server in mirror mode

        Returns:
            ldap modification result
        """
        return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                {"olcMirrorMode": [MODIFY_ADD, ["TRUE"]]})

    def removeProvider(self, raddr):
        """This function removes provider form server
        
        Args:
            raddr (string): provider uri, for example: ldaps://ldp.foo.org:1636

        Returns:
            -1 if this server has no such provider else 
            ldap modifcation result for removing provider
        """

        rmMirrorMode = False

        if len(self.getProviders()) <= 1:
            rmMirrorMode = True

        #if there is no such privder return -1
        if not self.conn.response:
            return -1

        #iterate all attributes to find basedn of olcSyncrepl
        for pr in self.conn.response:
            if pr["attributes"]["olcSyncrepl"]:
                for pri in pr["attributes"]["olcSyncrepl"]:
                    for l in pri.split():
                        ls = l.split('=')
                        if ls[0] == 'provider':
                            if ls[1] == raddr:
                                baseDn = pr['dn']
                                r = self.conn.modify(
                                    baseDn,
                                    {'olcSyncrepl': [MODIFY_DELETE, [pri]]})
                                if r:
                                    if rmMirrorMode:
                                        self.removeMirrorMode()
                                return r

    def add_provider(self, rid, raddr, rbinddn, rcredentials):
        """Adds provider to server for replication.

        Args:
            rid (int): provider server id
            raddr (string): provider uri, for example: ldaps://ldp.foo.org:1636
            rbindn (string): bind dn of replicator user
            rcredentials (string): password for replicator user (rbinddn)

        Returns:
            modification result of adding provider
        """

        #this is rpvider information
        ridText = (
            'rid={0} provider={1} bindmethod=simple binddn="{2}" '
            'tls_reqcert=never credentials={3} searchbase="o=gluu" '
            'logbase="cn=accesslog" '
            #'filter=(&(objectClass=*)(!(ou:dn:=appliances))) '
            'logfilter="(&(objectClass=auditWriteObject)(reqResult=0))" '
            'schemachecking=on type=refreshAndPersist retry="60 +" '
            'syncdata=accesslog sizeLimit=unlimited '
            'timelimit=unlimited'.format(rid, raddr, rbinddn, rcredentials))

        #we should delete if such an entry exists, so search it
        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcSyncRepl"])

        # delete the entry if a syncrepl config exists for the same rid
        entry = self.conn.entries[0]
        for rep in entry["olcSyncRepl"]:
            if 'rid={0}'.format(rid) in rep:
                lmod = {"olcSyncRepl": [(MODIFY_DELETE, [rep])]}
                self.conn.modify('olcDatabase={1}mdb,cn=config', lmod)
                break

        mod = {"olcSyncRepl": [(MODIFY_ADD, [ridText])]}

        return self.conn.modify('olcDatabase={1}mdb,cn=config', mod)

    def checkAccesslogDB(self):
        """Checks if access logdb (cn=accesslog) entry exists
        
        Returns:
            search results of cn=accesslog
        """

        return self.conn.search(search_base='cn=config',
                                search_filter='(olcSuffix=cn=accesslog)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def addTestUser(self, cn, sn, mail):
        """Adds test user
        
        Args:
            cn (string): common name for test user
            sn (string): last name for test user
            mail (string): mail address for test user
            
        Returns:
            ldap add result
        """

        #get base dn
        self.checkBaseDN()

        #check if base for tests user exists 'ou=testusers,o=gluu'
        self.checkTestUserBase()

        #create a uid
        uid = '{0}@{1}'.format(time.time(), self.hostname)

        #make dn for test user
        dn = "uid={0},ou=testusers,o=gluu".format(uid)

        return self.conn.add(dn,
                             attributes={
                                 'objectClass': ['top', 'inetOrgPerson'],
                                 "cn": cn,
                                 'mail': mail,
                                 'sn': sn,
                                 'title': 'gluuClusterMgrTestUser',
                                 'uid': uid
                             })

    def checkTestUserBase(self):
        """Checks if test user base (ou=testusers,o=gluu) exists. If not exists
            creates it
            
        Returns:
            None if not base dn exists else returns ldap add result
            ldap add result
        """

        if not self.conn.search(search_base='ou=testusers,o=gluu',
                                search_filter='(objectClass=inetOrgPerson)',
                                search_scope=BASE,
                                attributes='*'):
            self.conn.add('ou=testusers,o=gluu',
                          attributes={
                              'objectClass': ['top', 'organizationalUnit'],
                              'ou': 'testusers',
                          })

    def searchTestUsers(self):
        """Searches test user
            
        Returns:
            ldap search result for test users
        """
        return self.conn.search(search_base='ou=testusers,o=gluu',
                                search_filter='(title=gluuClusterMgrTestUser)',
                                search_scope=LEVEL,
                                attributes='*')

    def delDn(self, dn):
        """Deltes given dn
        
        Args:
            dn (string): dn to be deleted
            
        Returns:
            ldap delete result
        """

        return self.conn.delete(dn)

    def getProviders(self):
        """Collects providers for this server

        Returns:
            provider dictionary.
        """

        pDict = {}

        #Search provider entries
        if self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                            search_filter='(objectClass=*)',
                            search_scope=BASE,
                            attributes=["olcSyncRepl"]):

            #Iterate all providers and parse it
            for pe in self.conn.response[0]['attributes']['olcSyncrepl']:
                for e in pe.split():
                    es = e.split("=")
                    if re.search('(\{\d*\})*rid', es[0]):
                        pid = es[1]
                    elif es[0] == 'provider':
                        host, port = get_host_port(es[1])
                        dkey = host
                        if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",
                                    host):
                            dkey = get_hostname_by_ip(host)

                pDict[dkey] = (pid, port, host)

        return pDict

    def getMMRStatus(self):
        """Returns multi master replication status for this server

        Returns:
            dictionary includes replicator results
        """

        retDict = {}
        retDict["server_id"] = None
        if self.checkServerID():
            if self.conn.response[0]['attributes']['olcServerID']:
                retDict["server_id"] = self.conn.response[0]['attributes'][
                    'olcServerID'][0]

        retDict["overlaysDB1"] = self.checkSyncprovOverlaysDB1()
        retDict["overlaysDB2"] = self.checkSyncprovOverlaysDB2()
        retDict["mirrorMode"] = self.checkMirroMode()
        retDict["accesslogDB"] = self.checkAccesslogDBEntry()
        retDict["accesslogPurge"] = self.checkAccesslogPurge()
        retDict["providers"] = self.getProviders()

        return retDict

    def getMainDbDN(self):
        """Returns dn of main db 

        Returns:
            dn of main db
        """

        if self.conn.search(
                search_base="cn=config",
                search_scope=LEVEL,
                search_filter="(olcDbDirectory=/opt/gluu/data/main_db)",
                attributes='*'):
            if self.conn.response:
                return self.conn.response[0]['dn']

    def setLimitOnMainDb(self, replicator_dn):
        """Sets limit for replicator dn
        
        Args:
            replicator_dn (string): dn for replicator user

        Returns:
            ldap modification result
        """

        main_db_dn = self.getMainDbDN()
        return self.conn.modify(
            main_db_dn, {
                'olcLimits': [
                    MODIFY_ADD,
                    'dn.exact="{0}" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited'
                    .format(replicator_dn)
                ]
            })

    def addReplicatorUser(self, replicator_dn, passwd):
        """Adds replicator user (dn)
        
        Args:
            replicator_dn (string): dn for replicator user
            passwd (string): password of replicator user

        Returns:
            ldap add/modification result
        """

        #Ckech if base dn exists
        self.checkBaseDN()

        #get encoded password
        enc_passwd = ldap_encode(passwd)

        #check if replicator user exists
        self.conn.search(replicator_dn,
                         search_filter='(objectClass=*)',
                         search_scope=BASE)

        if len(self.conn.response):  # user dn already exists
            return self.conn.modify(
                replicator_dn, {"userPassword": [MODIFY_REPLACE, enc_passwd]})
        else:
            m = re.search('cn=(?P<cn>[a-zA-Z][a-zA-Z ]*[a-zA-Z]),o=gluu',
                          replicator_dn)
            cn = m.group('cn')
            attributes = {
                'objectClass': ['top', 'inetOrgPerson'],
                'cn': cn,
                'sn': 'replicator',
                'uid': 'replicator',
                'userpassword': enc_passwd,
            }
            return self.conn.add(replicator_dn, attributes=attributes)

    def checkBaseDN(self):
        """Checks id base dn exists. If not creates

        Returns:
            ldap add result
        """
        r = self.conn.search(search_base="o=gluu",
                             search_filter='(objectClass=top)',
                             search_scope=BASE)
        if not self.conn.search(search_base="o=gluu",
                                search_filter='(objectClass=top)',
                                search_scope=BASE):
            logger.info("Adding base DN")
            self.conn.add('o=gluu',
                          attributes={
                              'objectClass': ['organization'],
                              'o': 'gluu',
                          })

    def configureOxIDPAuthentication(self, servers):
        """Makes gluu server aware of all ldap servers in the cluster

        Args:
            servers (list): list of server to add oxIDPAuthentication

        Returns:
            ldap modify result
        """

        if self.conn.search("ou=appliances,o=gluu",
                            search_filter='(objectClass=gluuAppliance)',
                            search_scope=LEVEL,
                            attributes=["oxIDPAuthentication"]):
            r = self.conn.response
            if r:
                oxidp_s = r[0]["attributes"]["oxIDPAuthentication"][0]
                oxidp = json.loads(oxidp_s)
                config = json.loads(oxidp["config"])
                config["servers"] = servers
                oxidp["config"] = json.dumps(config)
                oxidp_s = json.dumps(oxidp)
                return self.conn.modify(
                    r[0]['dn'],
                    {"oxIDPAuthentication": [MODIFY_REPLACE, oxidp_s]})
Exemple #21
0
#	"uid": "*****@*****.**",
#	"homeDirectory": "/home/mariana",
#	"UserPassword": "******" + b2a_base64(md5json).decode("utf-8")
#}
#objectClass = ["top","person","inetOrgPerson","posixaccount","organizationPerson"]
#dn = "uid=%s,dc=dexter,dc=com,dc=br"%(user["mail"])
#user_added = ldap_con.add(dn, objectClass, user)
#print(user_added)

#email = "*****@*****.**"
#dn = "uid=%s,dc=dexter,dc=com,dc=br"%(email)
#ldap_con.search(
#	dn,"(objectclass=person)",attributes=["sn", "userPassword"]
#)
#print(ldap_con.entries)

#email = "*****@*****.**"
#dn = "uid=%s,dc=dexter,dc=com,dc=br"%(email)
#changes = {
#	"cn": [(MODIFY_REPLACE, ["maria"])],
#	"sn": [(MODIFY_REPLACE, ["araújo"])]
#}
#ldap_con.modify(
#	dn,changes
#)
#print(ldap_con.result)

email = "*****@*****.**"
dn = "uid=%s,dc=dexter,dc=com,dc=br" % (email)
print(ldap_con.delete(dn))
Exemple #22
0
def compare_to_ldap(sis_users, needs_sis_username=0):
    server = Server(host='www.xxx.yyy.zzz',
                    port=636,
                    use_ssl=True,
                    get_info=NONE)
    # logging.info('Please enter your LDAP username: '******'myname'
    password = '******'
    attempts = 0
    conn = Connection(server,
                      user='******' + login_name + ',o=xyz',
                      password=password)
    conn.bind()

    while conn.result['description'] == 'invalidCredentials':
        logging.info('Incorrect username or password. Please try again.')
        logging.info('Please enter your LDAP username: '******'CN=' + login_name + ',o=xyz',
                          password=password)
        conn.bind()

    logging.info('LDAP Login successful')

    ldap_un_list = []

    logging.info('\n')
    search_filter = '(objectclass=Person)'
    for i in range(3, 5):
        curr_grade = 'Grade-PK' + str(i)
        search_base = 'ou=' + curr_grade + ',o=xyz'
        logging.info('Searching ' + curr_grade)
        conn.search(search_base=search_base,
                    search_filter=search_filter,
                    search_scope=SUBTREE,
                    attributes=['uid'])

        for entry in conn.entries:
            uid = entry['uid'].value
            ldap_un_list.append(uid.lower())

    for i in range(0, 13):
        curr_grade = 'Grade-' + str(i).zfill(2)
        search_base = 'ou=' + curr_grade + ',o=xyz'
        logging.info('Searching ' + curr_grade)
        conn.search(search_base=search_base,
                    search_filter=search_filter,
                    search_scope=SUBTREE,
                    attributes=['uid'])

        for entry in conn.entries:
            uid = entry['uid'].value
            ldap_un_list.append(uid.lower())

    ldap_un_list.sort()

    # *** Add any potential chromebook enrollment accounts here if necessary. e.g., PK3 or PK4
    exclusion_list = [
        'PK3', 'PK4', '00', '1st', '2nd', '3rd', '4th', '5th', '6th', '7th',
        '8th', '9th', '10th', '11th', '12th', 'billybob'
    ]
    for name in exclusion_list:
        if name in ldap_un_list:
            ldap_un_list.remove(name)

    if needs_sis_username == 1:
        return ldap_un_list

    logging.info('\n' + str(len(ldap_un_list)) +
                 ' total students in LDAP, Grades PK3-12.')
    with open('ldap_un_list.log', mode='w') as file:
        for student in ldap_un_list:
            file.write(student + '\n')
    needs_deletion = []

    for student in ldap_un_list:
        if student in sis_users.keys():
            continue
        needs_deletion.append(student)

    logging.info('\nStudents who need to be deleted from LDAP:')
    logging.info(needs_deletion)
    logging.info('\n' + str(len(needs_deletion)) +
                 ' accounts recommended to be deleted.')

    needs_account = OrderedDict()
    for student in sis_users.keys():
        conn.search(search_base='o=xyz',
                    search_filter='(uid=' + student + ')',
                    search_scope=SUBTREE)
        if len(conn.entries) > 0:
            continue
        needs_account[student] = sis_users[student]

    logging.info('\nStudents who need to be added to LDAP:')
    logging.info(needs_account.keys())
    logging.info('\n' + str(len(needs_account)) +
                 ' accounts to be created in LDAP.')

    if len(needs_deletion) == 0:
        logging.info('No accounts need to be deleted.')
    else:
        error_count = 0
        # User exists in LDAP but not SIS -> we can delete them from LDAP
        for username in needs_deletion:
            conn.search(search_base='o=xyz',
                        search_filter='(uid=' + username + ')')
            user = conn.entries[0].entry_dn
            conn.delete(user)
            if str(conn.result['description']) == 'success':
                logging.info('Success - ' + username + ' deleted.')
            else:
                logging.info('Error - ' + username + ' could not be deleted.')
                error_count += 1
            logging.info('\n')
        logging.info('\nAccount deletion process completed with ' +
                     str(error_count) + ' errors.')

    pass_list = create_ldap_accounts(needs_account)
    update_students_in_sis(needs_account, pass_list)
    conn.unbind()
class Test(unittest.TestCase):
    def setUp(self):
        server = Server(host=test_server, port=test_port, allowed_referral_hosts=('*', True))
        self.connection = Connection(server, auto_bind=True, version=3, client_strategy=test_strategy, user=test_user, password=test_password, authentication=test_authentication, lazy=test_lazy_connection, pool_name='pool1')

    def tearDown(self):
        self.connection.unbind()
        if self.connection.strategy_type == STRATEGY_REUSABLE_THREADED:
            self.connection.strategy.terminate()
        self.assertFalse(self.connection.bound)

    def test_modify_dn_operation(self):
        result = self.connection.delete(dn_for_test(test_base, 'test-add-modified-dn'))
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject'])

        result = self.connection.delete(dn_for_test(test_base, 'test-add-for-modify-dn'))
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject'])

        result = self.connection.add(dn_for_test(test_base, 'test-add-for-modify-dn'), [], {'objectClass': 'iNetOrgPerson', 'sn': 'test-compare', 'givenName': 'modify-dn'})
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'entryAlreadyExists'])

        result = self.connection.modify_dn(dn_for_test(test_base, 'test-add-for-modify-dn'), test_name_attr + '=test-add-modified-dn')
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject'])

    def test_move_dn(self):
        result = self.connection.delete(dn_for_test(test_base, 'test-add-for-move-dn'))
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject'])

        result = self.connection.add(dn_for_test(test_base, 'test-add-for-move-dn'), [], {'objectClass': 'iNetOrgPerson', 'sn': 'test-add-for-move-dn', 'givenName': 'move-dn'})
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'entryAlreadyExists'])

        result = self.connection.delete(dn_for_test(test_moved, 'test-add-for-move-dn'))
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['success', 'noSuchObject', 'busy'])

        result = self.connection.modify_dn(dn_for_test(test_base, 'test-add-for-move-dn'), test_name_attr + '=test-add-for-move-dn', new_superior=test_moved)
        if not isinstance(result, bool):
            response, result = self.connection.get_response(result)
        else:
            response = self.connection.response
            result = self.connection.result
        self.assertTrue(result['description'] in ['other', 'success', 'entryAlreadyExists', 'noSuchObject'])
Exemple #24
0
class LDAPLib:
    def __init__(self):
        self.connection = Connection(
            server=settings.LDAP_SERVER,
            user=settings.LDAP_USER,
            password=settings.LDAP_PASSWORD,
            auto_bind=True,
        )

        self.user_base = ",".join(("ou=users", settings.LDAP_BASE_DN))
        self.group_base = ",".join(("ou=groups", settings.LDAP_BASE_DN))

        self.user_attributes = ["uid", "cn", "sn"]
        self.group_attributes = ["gidNumber", "cn"]

    def get_all_users(self):
        search_filter = "(cn=*)"
        result = self.connection.search(self.user_base,
                                        search_filter,
                                        attributes=self.user_attributes)
        if result:
            return self.connection.entries
        return []

    def get_all_groups(self):
        search_filter = "(cn=*)"
        result = self.connection.search(self.group_base,
                                        search_filter,
                                        attributes=self.group_attributes)
        if result:
            return self.connection.entries
        return []

    def search_user(self, query):
        search_filter = f"(uid={query})"
        result = self.connection.search(self.user_base,
                                        search_filter,
                                        attributes=self.user_attributes)
        if result:
            return self.connection.entries[0]

    def add_user(self, uid, first_name, last_name, email, password_hash):
        dn = ",".join((f"uid={uid}", self.user_base))
        return self.connection.add(
            dn,
            ["inetOrgPerson", "top"],
            {
                "uid": uid,
                "cn": first_name,
                "sn": last_name,
                "mail": email,
                "userPassword": "******" + password_hash,
            },
        )

    def search_group(self, query):
        search_filter = f"(gidNumber={query})"
        result = self.connection.search(self.group_base,
                                        search_filter,
                                        attributes=self.group_attributes)
        if result:
            return self.connection.entries[0]

    def add_group(self, gid, name):
        dn = ",".join((f"cn={name}", self.group_base))
        return self.connection.add(dn, ["posixGroup", "top"], {
            "gidNumber": gid,
            "cn": name
        })

    def update_organization_unit(self, name):
        """
        Make sure the organization unit exists in LDAP.
        """
        search_filter = f"(ou={name})"
        result = self.connection.search(settings.LDAP_BASE_DN,
                                        search_filter,
                                        attributes=["ou"])
        if not result:
            dn = ",".join((f"ou={name}", settings.LDAP_BASE_DN))
            self.connection.add(dn, ["organizationalUnit", "top"],
                                {"ou": name})

    def delete_user(self, uid):
        dn = ",".join((f"uid={uid}", self.user_base))
        return self.connection.delete(dn)

    def delete_group(self, cn):
        dn = ",".join((f"cn={cn}", self.group_base))
        return self.connection.delete(dn)

    def check_password(self, uid, password_hash):
        dn = ",".join((f"uid={uid}", self.user_base))
        return self.connection.compare(dn, "userPassword",
                                       "{CRYPT}" + password_hash)

    def change_password(self, uid, password_hash):
        dn = ",".join((f"uid={uid}", self.user_base))

        changes = {
            "userPassword": [(MODIFY_REPLACE, ["{CRYPT}" + password_hash])]
        }
        self.connection.modify(dn, changes)

    def update_group_members(self, cn, members):
        dn = ",".join((f"cn={cn}", self.group_base))

        if members:
            changes = {"memberUid": [(MODIFY_REPLACE, members)]}
        else:
            changes = {"memberUid": [(MODIFY_DELETE, [])]}

        self.connection.modify(dn, changes)
Exemple #25
0
class LdapOperations:
    def __init__(self):
        self.conn = None
        self.loggedUsers = []
        self.connect()

    def connect(self):
        server = Server(URL, get_info=ALL)
        self.conn = Connection(server, USER, PASS, auto_bind=True)
        self.conn.bind()

    def add_user(self, commonName, username, password):
        test = self.conn.add(
            'cn={},ou=myUsers,cn=admin,dc=projet,dc=com'.format(commonName),
            ['inetOrgPerson', 'top'], {
                'objectClass': 'person',
                'sn': username,
                'userPassword': password
            })
        print(test)
        if test:
            return self.generate_selfsigned_cert(commonName)
        else:
            return None, None

    def delete_user(self, commonName):
        dn = "cn={},ou=myUsers,cn=admin,dc=projet,dc=com".format(commonName)
        res = self.conn.delete(dn)
        print(res)

    def get_all_users(self):
        self.conn.search(SEARCH, FILTER)
        #print(self.conn.entries)

    def check_login_infos(self, commonName, password):
        commonName = commonName.strip()
        password = password.strip()
        filterr = '(&(cn={0})(userPassword={1}))'.format(commonName, password)
        self.conn.search(SEARCH, filterr)
        #print(self.conn.entries)
        return len(self.conn.entries) > 0

    def check_certificate(self, cn, certif_string):
        with open('{}tmp/{}.crt'.format(PROJECT_DIRECTORY, cn), 'wb') as f:
            f.write(str.encode(certif_string))
        completedProcess = subprocess.run(
            'openssl verify -CAfile {}ca/ca.crt {}tmp/{}.crt'.format(
                PROJECT_DIRECTORY, PROJECT_DIRECTORY, cn),
            shell=True)
        return completedProcess.returncode == 0

    def generate_selfsigned_cert(self, cn):
        key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend(),
        )

        public_key = key.public_key()
        pem = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo)
        #write our public to disk
        with open("{}rsa_keys/{}public_key.pem".format(PROJECT_DIRECTORY, cn),
                  "wb") as f:
            f.write(pem)

        key_pem = key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        )
        # Write our private key to disk for safe keeping
        with open("{}rsa_keys/{}_Key.pem".format(PROJECT_DIRECTORY, cn),
                  "wb") as f:
            f.write(key_pem)

        # Generate a CSR
        csr = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                # Provide various details about who we are.
                x509.NameAttribute(NameOID.COUNTRY_NAME, u"TN"),
                x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"INSAT"),
                x509.NameAttribute(NameOID.LOCALITY_NAME, u"INSAT"),
                x509.NameAttribute(NameOID.ORGANIZATION_NAME, cn),
                x509.NameAttribute(NameOID.COMMON_NAME, cn + '@insat.com'),
            ])).add_extension(
                x509.SubjectAlternativeName([
                    # Describe what sites we want this certificate for.
                    x509.DNSName(cn + '@insat.com')
                ]),
                critical=False,
                # Sign the CSR with our private key.
            ).sign(key, hashes.SHA256(), default_backend())
        # Create the client certificate
        pem_cert = open('{}ca/ca.crt'.format(PROJECT_DIRECTORY), 'rb').read()
        ca = x509.load_pem_x509_certificate(pem_cert, default_backend())
        #print(ca)
        pem_key = open('{}ca/ca.key'.format(PROJECT_DIRECTORY), 'rb').read()
        ca_key = serialization.load_pem_private_key(pem_key,
                                                    password=None,
                                                    backend=default_backend())
        #print(ca_key)

        builder = x509.CertificateBuilder()
        builder = builder.subject_name(csr.subject)
        builder = builder.issuer_name(ca.subject)
        builder = builder.not_valid_before(datetime.datetime.now() +
                                           datetime.timedelta(-1))
        builder = builder.not_valid_after(datetime.datetime.now() +
                                          datetime.timedelta(7))
        builder = builder.public_key(csr.public_key())
        builder = builder.serial_number(int(uuid.uuid4()))
        for ext in csr.extensions:
            builder.add_extension(ext.value, ext.critical)

        certificate = builder.sign(private_key=ca_key,
                                   algorithm=hashes.SHA256(),
                                   backend=default_backend())
        with open('{}certificates/{}.crt'.format(PROJECT_DIRECTORY, cn),
                  'wb') as f:
            f.write(certificate.public_bytes(serialization.Encoding.PEM))
        return certificate.public_bytes(serialization.Encoding.PEM), key_pem

    def login(self, commonName, password, certif):
        if self.check_login_infos(commonName, password) == False:
            return False, 'Wrong Password'
        if self.check_certificate(commonName, certif) == False:
            return False, 'Invalid Certificate'
        return True, 'you are logged'

    def get_public_key(self, commonName):
        public_key = open(
            "{}rsa_keys/{}public_key.pem".format(PROJECT_DIRECTORY,
                                                 commonName), "rb").read()
        # public_key = serialization.load_pem_public_key(pem_key,backend=default_backend())
        return public_key
Exemple #26
0
class LdapManager():
    dns_node_attrs = ['dn', 'dc', 'name', 'dnsRecord', 'dNSTombstoned']
    """
    This method allows the use of nt passwords as well as hashes in the format lmhash:nthash
    """
    def __init__(self,
                 server,
                 port=389,
                 ssl=False,
                 kerberos=False,
                 ntuser=None,
                 ntpass=None):
        self._server = server
        self._port = port
        self._ssl = ssl
        self._kerberos = kerberos
        self._ntuser = ntuser
        self._ntpass = ntpass
        self._delay = 1
        self._page_records = None
        self._query_delay = 1
        self._timeout = None
        self._base = ''

    def get_dn(self, domain):
        dn = ''
        for part in domain.split('.'):
            dn += 'DC=' + part + ','

        return dn[:-1]

    def connect(self):
        """ 
            LDAP connection method.

            Connects to an AD LDAP server via NTLM or KERBEROS authentication.
            This method supports the use of a plaintext password as well as 
            NTLM hashes.
            HASH = LMHASH:NTHASH

            Returns
            -------
            bool
                Returns the state of the connection.
        """
        logging.info('[*] Creating an LDAP connection.')
        s = Server(self._server,
                   port=self._port,
                   use_ssl=self._ssl,
                   connect_timeout=self._timeout,
                   get_info=ALL)

        if self._kerberos:
            # Use Kerberos
            logging.info('[*] Using KERBEROS authentication.')
            try:
                self._conn = Connection(s,
                                        auto_bind=AUTO_BIND_NONE,
                                        authentication=SASL,
                                        sasl_mechanism=KERBEROS,
                                        read_only=False,
                                        return_empty_attributes=True)
                bind_result = self._conn.bind()
            except Exception as e:
                msg = e.message.encode('ascii', 'ignore')
                if msg.lower(
                ).find('no kerberos credentials available (default cache: file:/tmp/'
                       ) != -1:
                    logging.error('[-] Kerberos binding error.')
                    logging.debug(
                        '[D] You need a valid kerberos TGT ticket or an LDAP TGS ticket for accessing the remote service.'
                    )
                    logging.debug(
                        '[D] Save the kerberos ticket ccache DB into /tmp/krb5cc_<uid> and try again.'
                    )
                elif msg.lower().find('ticket expired') != -1:
                    logging.error('[-] Kerberos binding error.')
                    logging.debug(
                        '[D] The provided Ticket has expired. Please provide a valid one and try again.'
                    )
                else:
                    logging.error('[-] Unexpected binding error.')
                    logging.debug('[D] Observe the following traceback:')
                    logging.debug(traceback.format_exc())
                return False
        else:
            # Use NTLM authentication
            logging.info('[*] Using NTLM authentication.')
            try:
                self._conn = Connection(s,
                                        auto_bind=AUTO_BIND_NONE,
                                        authentication=NTLM,
                                        user=self._ntuser,
                                        password=self._ntpass,
                                        read_only=False)
                bind_result = self._conn.bind()
            except:
                logging.error('[-] Unexpected binding error.')
                logging.debug('[D] Observe the following traceback:')
                logging.debug(traceback.format_exc())
                return False

        # Takes the root naming context
        if bind_result:
            self._base = json.loads(
                s.info.to_json())['raw']['rootDomainNamingContext'][0]
            logging.debug('[D] Binding base: %s' % self._base)
        else:
            logging.debug('[D] An error ocurred during the binding process.')
            logging.debug('[D] Ldap API Message: %s' %
                          self._conn.result['description'])
            return False

        return True

    def generic_search(self, base, s_filter, attributes):
        """
            Performs a generic LDAP search.

            Returns
                list[] : A JSON formatted list of objected.
        """
        if base is None:
            base = self._base
        if attributes is None:
            attributes = ALL_ATTRIBUTES
        json_list = []
        cookie = None
        while cookie == None or cookie != '':
            try:
                logging.debug('[D] Searching on base: %s' % base)
                logging.debug('[D] Searching with filter: %s' % s_filter)
                logging.debug('[D] Searching the attributes: %s' % attributes)
                self._conn.search(search_base=base,
                                  search_filter=s_filter,
                                  search_scope=SUBTREE,
                                  attributes=attributes,
                                  paged_size=self._page_records,
                                  paged_cookie=cookie)
            except ldap3.core.exceptions.LDAPInvalidFilterError as e:
                logging.error('[-] Search error: invalid LDAP query filter')
                logging.debug('[D] %s' % e.message)
                logging.debug('[D] Filter: %s' % s_filter)
                break
            except Exception:
                logging.error('[-] Unexpected search error.')
                logging.debug('[D] Observe the following traceback:')
                logging.debug(traceback.format_exc())
                break

            try:
                cookie = self._conn.result['controls'][
                    '1.2.840.113556.1.4.319']['value']['cookie']
            except:
                cookie = ''
            # Waits for n seconds before querying again
            time.sleep(self._query_delay)
            for entry in self._conn.entries:
                # Adds the new records to the list of json objects
                json_list.append(json.loads(entry.entry_to_json()))

        return json_list

    def get_dns_node_info(self, domain, name):
        """
            Gets DNS nodes.
        
            Returns
                list[]
                    A JSON formatted list of dnsNode class LDAP objects with ceretain attributes.
        """
        # It uses the full dn (including the name) because it requires to identify those nodes for
        # which the authenticated user does NOT have privileges and their attributes cannot be obtained
        domain_dn = self.get_dn(domain)
        node_dn = 'DC=' + name + ',DC=' + domain + ',CN=MicrosoftDNS,DC=DomainDnsZones,' + domain_dn
        search_filter = '(objectClass=*)'
        dns_nodes = self.generic_search(node_dn, search_filter,
                                        LdapManager.dns_node_attrs[1:])

        dns_record_list = []
        if len(dns_nodes) != 0:
            if dns_nodes[0]['attributes']['name'] == []:
                logging.debug(
                    '[D] Looks like you don\'t have permissions to read the node\'s attributes...'
                )
            else:
                dns_records = dns_nodes[0]['attributes']['dnsRecord']
                for dns_record in dns_records:
                    dns_record = base64.b64decode(dns_record['encoded'])
                    pair = []
                    pair.append(DNSRecord.getTypeFromDNSRecord(dns_record))
                    pair.append(DNSRecord.getDataFromDNSRecord(dns_record))
                    dns_record_list.append(pair)

        return dns_record_list

    def delete_dns_node(self, domain, name):
        """
            Removes a DNS node.
        
            Returns
                Result of operation
                    Status
        """
        domain_dn = self.get_dn(domain)
        node_dn = 'DC=' + name + ',DC=' + domain + ',CN=MicrosoftDNS,DC=DomainDnsZones,' + domain_dn

        try:
            logging.debug('[D] About to remove DNS node at dn: %s' % node_dn)
            self._conn.delete(node_dn)
            logging.debug('[D] LDAP Result: ' +
                          self._conn.result['description'])
        except Exception:
            logging.error('[-] Unexpected search error.')
            logging.debug('[D] Observe the following traceback:')
            logging.debug(traceback.format_exc())
            return False

        if self._conn.result['description'] == 'noSuchObject':
            print '|[*] The specified object does not exist.'
            return False
        else:
            print '[*] The object was successfully removed.'
            return True

    def add_dns_node(self, domain, dns_server, name, attacker_ip):
        """
            Adds a DNS node.
        
            Returns
                Boolean
                    Status
        """
        domain_dn = self.get_dn(domain)
        node_dn = 'DC=' + name + ',DC=' + domain + ',CN=MicrosoftDNS,DC=DomainDnsZones,' + domain_dn
        objectClass = ['top', 'dnsNode']
        dnsRecord = DNSRecord(attacker_ip, dns_server, domain)

        attributes = {
            'dNSTombstoned': True,
            'dnsRecord': dnsRecord.getRecord()
        }
        try:
            logging.debug('[D] About to add DNS node at dn: %s' % node_dn)
            self._conn.add(node_dn, objectClass, attributes)
            logging.debug('[D] LDAP Result: ' +
                          self._conn.result['description'])
        except Exception:
            logging.error('[-] Unexpected search error.')
            logging.debug('[D] Observe the following traceback:')
            logging.debug(traceback.format_exc())
            return False

        if self._conn.result['description'] == 'noSuchObject':
            print '[*] The specified object does not exist.'
            return False
        else:
            print '[*] The object was successfully added.'
            return True
Exemple #27
0
class OpenLDAPSession(object):
    __slots__ = ['uri', 'basedn', 'manager', 'passwd', 'ou_people', 'ou_groups', 'ou_hosts', 'ou_host_groups',
                 'ou_commands', 'ou_command_groups', 'ou_services', 'server', 'connection', 'event_handlers']

    EVENT_ON_USER_CREATED = 'on_user_created'
    EVENT_ON_USER_MODIFIED = 'on_user_modified'
    EVENT_ON_USER_DELETED = 'on_user_deleted'
    EVENT_ON_GROUP_CREATED = 'on_group_created'
    EVENT_ON_GROUP_MODIFIED = 'on_group_modified'
    EVENT_ON_GROUP_DELETED = 'on_group_deleted'
    EVENT_ON_HOST_CREATED = 'on_host_created'
    EVENT_ON_HOST_MODIFIED = 'on_host_modified'
    EVENT_ON_HOST_DELETED = 'on_host_deleted'
    EVENT_ON_HOSTGROUP_CREATED = 'on_hostgroup_created'
    EVENT_ON_HOSTGROUP_MODIFIED = 'on_hostgroup_modified'
    EVENT_ON_HOSTGROUP_DELETED = 'on_hostgroup_deleted'
    EVENT_ON_COMMAND_CREATED = 'on_command_created'
    EVENT_ON_COMMAND_MODIFIED = 'on_command_modified'
    EVENT_ON_COMMAND_DELETED = 'on_command_deleted'
    EVENT_ON_COMMANDGROUP_CREATED = 'on_commandgroup_created'
    EVENT_ON_COMMANDGROUP_MODIFIED = 'on_commandgroup_modified'
    EVENT_ON_COMMANDGROUP_DELETED = 'on_commandgroup_deleted'
    EVENT_ON_SERVICE_CREATED = 'on_service_created'
    EVENT_ON_SERVICE_MODIFIED = 'on_service_modified'
    EVENT_ON_SERVICE_DELETED = 'on_service_deleted'

    def __init__(self, uri, basedn, manager, passwd, start_tls=False, ou_people='people', ou_groups='groups', ou_hosts='hosts',
                 ou_host_groups='hostGroups', ou_commands='commands', ou_command_groups='commandGroups', ou_services='services',
                 event_handlers=None):
        self.uri = uri
        self.basedn = basedn
        self.manager = manager
        self.passwd = passwd
        self.ou_people = ou_people
        self.ou_groups = ou_groups
        self.ou_hosts = ou_hosts
        self.ou_host_groups = ou_host_groups
        self.ou_commands = ou_commands
        self.ou_command_groups = ou_command_groups
        self.ou_services = ou_services

        self.server = Server(self.uri, get_info=NONE)
        self.connection = Connection(self.server, user=self.manager, password=self.passwd, auto_bind=AUTO_BIND_TLS_BEFORE_BIND if start_tls else True)

        self.event_handlers = event_handlers
        if self.event_handlers is not None:
            for handler in self.event_handlers:
                handler.session = self

    def __enter__(self):
        self.connection.__enter__()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.connection.__exit__(exc_type, exc_val, exc_tb)

    @property
    def _placeholder(self):
        return 'cn=null,' + self.basedn

    @staticmethod
    def _get_entry_value(entry, key):
        if not hasattr(entry, key):
            return None
        return entry[key].value

    @staticmethod
    def _epoch_days():
        """
        days from 1970-01-01
        :return: int
        """
        return (time.utcnow() - datetime(1970, 1, 1)).days

    @staticmethod
    def _get_date_from_epoch_days(epoch):
        """
        get date from epoch days
        :param epoch: epoch days
        :return:
        """
        return (datetime(1970, 1, 1) + timedelta(days=epoch)).date()

    def _event_handler(self, event, *args, **kwargs):
        if self.event_handlers is not None:
            for event_handler in self.event_handlers:
                try:
                    getattr(event_handler, event)(*args, **kwargs)
                except:
                    logger.error_traceback(LOGGER_NAME)

    def construct_skeleton(self):
        """
        Build OpenLDAP skeleton
        :return:
        """
        for ou in self.ou_people, self.ou_groups, self.ou_hosts, self.ou_host_groups, self.ou_commands, self.ou_command_groups, self.ou_services:
            if not self.connection.search(self.basedn, '(ou=%s)' % ou):
                self.connection.add(','.join(('ou=' + ou, self.basedn)), ['organizationalUnit'], {'ou': ou})

    def assemble_dn(self, cn, ou):
        return ','.join(('cn=' + cn, 'ou=' + ou, self.basedn))

    def assemble_user_dn(self, cn):
        return self.assemble_dn(cn, self.ou_people)

    def assemble_group_dn(self, cn):
        return self.assemble_dn(cn, self.ou_groups)

    def assemble_host_dn(self, cn):
        return self.assemble_dn(cn, self.ou_hosts)

    def assemble_host_group_dn(self, cn):
        return self.assemble_dn(cn, self.ou_host_groups)

    def assemble_command_dn(self, cn):
        return self.assemble_dn(cn, self.ou_commands)

    def assemble_command_group_dn(self, cn):
        return self.assemble_dn(cn, self.ou_command_groups)

    def assemble_service_dn(self, cn):
        return self.assemble_dn(cn, self.ou_services)

    @staticmethod
    def extract_cn(dn):
        return dn.split(',')[0].split('=')[1]

    def search(self, search_base, search_filter, search_scope=SUBTREE, attributes=None):
        return self.connection.search(
            search_base=search_base,
            search_filter=search_filter,
            search_scope=search_scope,
            attributes=attributes,
        ), self.connection.entries

    def search_common(self, search_base, search_filter, search_scope=SUBTREE, attributes=None):
        success, entries = self.search(search_base, search_filter, search_scope=search_scope, attributes=attributes)
        ret = []
        if success:
            for entry in entries:
                obj = {'dn': entry.entry_dn}
                for k, v in entry.__dict__.items():
                    try:
                        obj[k] = v.value
                    except AttributeError:
                        pass
                ret.append(obj)
        return ret

    def search_users(self, search_filter, attributes=None):
        return self.search_common(','.join(('ou=' + self.ou_people, self.basedn)), search_filter, attributes=attributes)

    def search_groups(self, search_filter, attributes=None):
        return self.search_common(','.join(('ou=' + self.ou_groups, self.basedn)), search_filter, attributes=attributes)

    def search_hosts(self, search_filter, attributes=None):
        return self.search_common(','.join(('ou=' + self.ou_hosts, self.basedn)), search_filter, attributes=attributes)

    def search_host_groups(self, search_filter, attributes=None):
        results = self.search_common(','.join(('ou=' + self.ou_host_groups, self.basedn)), search_filter, attributes=attributes)
        if 'uniqueMember' in attributes:
            for result in results:
                if isinstance(result['uniqueMember'], six.string_types):
                    if result['uniqueMember'] == self._placeholder:
                        result['uniqueMember'] = []
                    else:
                        result['uniqueMember'] = [result['uniqueMember']]
                elif isinstance(result['uniqueMember'], collections.Iterable):
                    result['uniqueMember'] = list(filter(lambda x: x != self._placeholder, result['uniqueMember']))
        return results

    def search_commands(self, search_filter, attributes=None):
        results = self.search_common(','.join(('ou=' + self.ou_commands, self.basedn)), search_filter, attributes=attributes)
        if 'sudoCommand' in attributes:
            for result in results:
                if isinstance(result['sudoCommand'], six.string_types):
                    result['sudoCommand'] = [result['sudoCommand']]
        return results

    def search_command_groups(self, search_filter, attributes=None):
        results = self.search_common(','.join(('ou=' + self.ou_command_groups, self.basedn)), search_filter, attributes=attributes)
        if 'uniqueMember' in attributes:
            for result in results:
                if isinstance(result['uniqueMember'], six.string_types):
                    if result['uniqueMember'] == self._placeholder:
                        result['uniqueMember'] = []
                    else:
                        result['uniqueMember'] = [result['uniqueMember']]
                elif isinstance(result['uniqueMember'], collections.Iterable):
                    result['uniqueMember'] = list(filter(lambda x: x != self._placeholder, result['uniqueMember']))
        return results

    def search_services(self, search_filter, attributes=None):
        results = self.search_common(','.join(('ou=' + self.ou_services, self.basedn)), search_filter, attributes=attributes)
        if 'authorizedService' in attributes:
            for result in results:
                if isinstance(result['authorizedService'], six.string_types):
                    result['authorizedService'] = [result['authorizedService']]
        return results

    def get(self, dn, attributes=None):
        objs = self.search_common(dn, '(objectClass=*)', search_scope=BASE, attributes=attributes)
        return objs[0] if len(objs) > 0 else None

    def get_user(self, cn, attributes=None):
        users = self.search_users(search_filter='(&(objectClass=person)(cn=%s))' % cn, attributes=attributes)
        return users[0] if len(users) > 0 else None

    def get_group(self, cn, attributes=None):
        groups = self.search_groups(search_filter='(&(objectClass=posixGroup)(cn=%s))' % cn, attributes=attributes)
        return groups[0] if len(groups) > 0 else None

    def get_host(self, cn, attributes=None):
        hosts = self.search_hosts(search_filter='(&(objectClass=device)(cn=%s))' % cn, attributes=attributes)
        return hosts[0] if len(hosts) > 0 else None

    def get_host_group(self, cn, attributes=None):
        host_groups = self.search_host_groups(search_filter='(&(objectClass=groupOfUniqueNames)(cn=%s))' % cn, attributes=attributes)
        return host_groups[0] if len(host_groups) > 0 else None

    def get_command(self, cn, attributes=None):
        commands = self.search_commands(search_filter='(&(objectClass=sudoRole)(cn=%s))' % cn, attributes=attributes)
        return commands[0] if len(commands) > 0 else None

    def get_command_group(self, cn, attributes=None):
        command_groups = self.search_command_groups(search_filter='(&(objectClass=groupOfUniqueNames)(cn=%s))' % cn, attributes=attributes)
        return command_groups[0] if len(command_groups) > 0 else None

    def get_service(self, cn, attributes=None):
        services = self.search_services(search_filter='(&(objectClass=authorizedServiceObject)(cn=%s))' % cn, attributes=attributes)
        return services[0] if len(services) > 0 else None

    def add(self, dn, object_class=None, attributes=None, controls=None, event=None, skip_event_callback=False):
        result = self.connection.add(dn, object_class=object_class, attributes=attributes, controls=controls)
        if not skip_event_callback:
            self._event_handler(event, dn, object_class=object_class, attributes=attributes)
        return result

    def add_user(self, cn, sn, uid_number, gid_number=100, gecos=None, mail=None, display_name=None,
                 shadow_min=None, shadow_max=None, shadow_inactive=None, shadow_warning=None, shadow_last_change=None,
                 skip_event_callback=False):
        # check value
        if strings.is_blank(cn):
            raise Exception('cn cannot be blank')
        if strings.is_blank(sn):
            raise Exception('sn cannot be blank')
        if num.safe_int(uid_number) <= 1000:
            raise Exception('uidNumber should > 1000')

        attributes = {
            'cn': cn,
            'uid': cn,
            'sn': sn,
            'uidNumber': num.safe_int(uid_number),
            'gidNumber': num.safe_int(gid_number),
            'homeDirectory': '/home/' + cn,
            'loginShell': '/bin/bash',
            'userPassword': '******',
            'sudoUser': cn,
            'sudoHost': 'ALL',
            'sudoOption': '!authenticate',
        }
        if gecos is not None:
            attributes['gecos'] = gecos
        if mail is not None:
            attributes['mail'] = mail
        if display_name is not None:
            attributes['displayName'] = display_name
        if shadow_min is not None:
            attributes['shadowMin'] = shadow_min
        if shadow_max is not None:
            attributes['shadowMax'] = shadow_max
        if shadow_inactive is not None:
            attributes['shadowInactive'] = shadow_inactive
        if shadow_warning is not None:
            attributes['shadowWarning'] = shadow_warning
        if shadow_last_change is not None:
            attributes['shadowLastChange'] = shadow_last_change  # set 0 to force change password on the first login
        else:
            attributes['shadowLastChange'] = self._epoch_days()

        return self.add(
            dn=self.assemble_user_dn(cn),
            object_class=['top', 'posixAccount', 'shadowAccount', 'person', 'inetOrgPerson', 'hostObject', 'sudoRole', 'authorizedServiceObject'],
            attributes=attributes,
            event=self.EVENT_ON_USER_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_group(self, cn, gid_number, skip_event_callback=False):
        return self.add(
            dn=self.assemble_group_dn(cn),
            object_class=['top', 'posixGroup'],
            attributes={
                'gidNumber': gid_number
            },
            event=self.EVENT_ON_GROUP_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_host(self, cn, cn_list=None, ip_host_number=None, skip_event_callback=False):
        attributes = {}
        if strings.is_blank(cn):
            raise Exception("host cn can't be blank")
        if cn_list is not None and not isinstance(cn_list, collections.Iterable):
            raise Exception("host cn_list should be iterable or None")
        cn_list = set(cn_list).add(cn)
        attributes['cn'] = cn if cn_list is None else cn_list
        if ip_host_number is not None:
            attributes['ipHostNumber'] = ip_host_number
        return self.add(
            dn=self.assemble_host_dn(cn),
            object_class=['top', 'device', 'ipHost'],
            attributes=attributes,
            event=self.EVENT_ON_HOST_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_host_group(self, cn, skip_event_callback=False):
        return self.add(
            dn=self.assemble_host_group_dn(cn),
            object_class=['top', 'groupOfUniqueNames'],
            attributes={
                'cn': cn,
                'uniqueMember': [self._placeholder],
            },
            event=self.EVENT_ON_HOSTGROUP_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_command(self, cn, sudo_command=None, skip_event_callback=False):
        attributes = {
            'cn': cn,
            'sn': cn,
        }
        if sudo_command is not None:
            attributes['sudoCommand'] = sudo_command
        return self.add(
            dn=self.assemble_command_dn(cn),
            object_class=['top', 'person', 'sudoRole'],
            attributes=attributes,
            event=self.EVENT_ON_COMMAND_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_command_group(self, cn, skip_event_callback=False):
        return self.add(
            dn=self.assemble_command_group_dn(cn),
            object_class=['top', 'groupOfUniqueNames'],
            attributes={
                'cn': cn,
                'uniqueMember': [self._placeholder],
            },
            event=self.EVENT_ON_COMMANDGROUP_CREATED,
            skip_event_callback=skip_event_callback,
        )

    def add_service(self, cn, authorized_service=None, skip_event_callback=False):
        attributes = {
            'cn': cn,
            'sn': cn,
        }
        if authorized_service is not None:
            attributes['authorizedService'] = authorized_service
        return self.add(
            dn=self.assemble_service_dn(cn),
            object_class=['top', 'person', 'authorizedServiceObject'],
            attributes=attributes,
            event=self.EVENT_ON_SERVICE_CREATED,
            skip_event_callback=skip_event_callback,
        )

    @staticmethod
    def _make_changes(legal_attrs, **other_attrs):
        changes = {}
        for attribute, v in other_attrs.items():
            if not objects.contains(attribute, *legal_attrs):
                raise Exception('illegal attribute in "other_attrs": %s' % attribute)
            if v is None:
                changes[inflection.camelize(attribute, False)] = MODIFY_DELETE, []
            else:
                if isinstance(v, six.string_types):
                    changes[inflection.camelize(attribute, False)] = MODIFY_REPLACE, [v]
                elif isinstance(v, collections.Iterable):
                    changes[inflection.camelize(attribute, False)] = MODIFY_REPLACE, v
                else:
                    raise Exception('illegal type found for "%s"' % attribute)
        return changes

    def modify(self, dn, changes, controls=None, event=None, skip_event_callback=False):
        has_event = self.event_handlers is not None and event is not None and not skip_event_callback
        oldobject = None
        if has_event:
            oldobject = self.get(dn, ALL_ATTRIBUTES)
        result = self.connection.modify(dn, changes, controls=controls)
        if has_event:
            self._event_handler(event, dn, changes, oldobject=oldobject)
        return result

    def modify_user(self, cn, sn=None, uid_number=None, skip_event_callback=False, **other_attrs):
        changes = {}
        legal_attrs = ('gid_number', 'gecos', 'mail', 'display_name', 'shadow_min', 'shadow_max', 'shadow_inactive', 'shadow_warning', 'host', 'sudoCommand', 'authorizedService')
        if sn is not None:
            changes['sn'] = MODIFY_REPLACE, [sn]
        if uid_number is not None:
            changes['uidNumber'] = MODIFY_REPLACE, [uid_number]
        changes.update(self._make_changes(legal_attrs, **other_attrs))
        return self.modify(self.assemble_user_dn(cn), changes, event=self.EVENT_ON_USER_MODIFIED, skip_event_callback=skip_event_callback)

    def modify_group(self, cn, gid_number=None, skip_event_callback=False, **other_attrs):
        changes = {}
        legal_attrs = ('memberUid', 'description')
        if gid_number is not None:
            changes['gidNumber'] = MODIFY_REPLACE, [gid_number]
        changes.update(self._make_changes(legal_attrs, **other_attrs))
        return self.modify(self.assemble_group_dn(cn), changes, event=self.EVENT_ON_GROUP_MODIFIED, skip_event_callback=skip_event_callback)

    def modify_host(self, cn, skip_event_callback=False, **other_attrs):
        changes = {}
        cn_list = other_attrs.get('cn_list')
        if cn_list is not None:
            if not isinstance(cn_list, collections.Iterable):
                raise Exception("host cn_list should be iterable or None")
            cn_list = set(cn_list).add(cn)
            changes['cn'] = MODIFY_REPLACE, cn_list
        legal_attrs = ['ip_host_number']
        new_other_attrs = copy.deepcopy(other_attrs)
        if hasattr(new_other_attrs, 'cn_list'):
            del new_other_attrs['cn_list']
        changes.update(self._make_changes(legal_attrs, **new_other_attrs))
        return self.modify(self.assemble_host_dn(cn), changes, event=self.EVENT_ON_HOST_MODIFIED, skip_event_callback=skip_event_callback)

    def modify_host_group(self, cn, unique_member=None, skip_event_callback=False):
        changes = {}
        if unique_member is not None:
            if not (isinstance(unique_member, collections.Iterable) and isinstance(unique_member, collections.Sized)):
                raise Exception("host group unique_member should be iterable or None")
            if len(unique_member) == 0:
                changes['uniqueMember'] = MODIFY_REPLACE, [self._placeholder]
            else:
                changes['uniqueMember'] = MODIFY_REPLACE, list(map(lambda x: self.assemble_host_dn(x), unique_member))
            return self.modify(self.assemble_host_group_dn(cn), changes, event=self.EVENT_ON_HOSTGROUP_MODIFIED, skip_event_callback=skip_event_callback)

    def modify_command(self, cn, skip_event_callback=False, **other_attrs):
        return self.modify(
            self.assemble_command_dn(cn),
            self._make_changes(['sudoCommand'], **other_attrs),
            event=self.EVENT_ON_COMMAND_MODIFIED,
            skip_event_callback=skip_event_callback
        )

    def modify_command_group(self, cn, unique_member=None, skip_event_callback=False):
        changes = {}
        if unique_member is not None:
            if not (isinstance(unique_member, collections.Iterable) and isinstance(unique_member, collections.Sized)):
                raise Exception("command group unique_member should be iterable or None")
            if len(unique_member) == 0:
                changes['uniqueMember'] = MODIFY_REPLACE, [self._placeholder]
            else:
                changes['uniqueMember'] = MODIFY_REPLACE, list(map(lambda x: self.assemble_command_dn(x), unique_member))
            return self.modify(self.assemble_command_group_dn(cn), changes, event=self.EVENT_ON_COMMANDGROUP_MODIFIED, skip_event_callback=skip_event_callback)

    def modify_service(self, cn, skip_event_callback=False, **other_attrs):
        return self.modify(
            self.assemble_service_dn(cn),
            self._make_changes(['authorizedService'], **other_attrs),
            event=self.EVENT_ON_SERVICE_MODIFIED,
            skip_event_callback=skip_event_callback
        )

    def delete(self, dn, controls=None, event=None, skip_event_callback=False):
        has_event = self.event_handlers is not None and event is not None and not skip_event_callback
        oldobject = None
        if has_event:
            oldobject = self.get(dn, ALL_ATTRIBUTES)
        result = self.connection.delete(dn, controls=controls)
        if has_event:
            self._event_handler(event, dn, oldobject=oldobject)
        return result

    def delete_user(self, cn, skip_event_callback=False):
        return self.delete(self.assemble_user_dn(cn), event=self.EVENT_ON_USER_DELETED, skip_event_callback=skip_event_callback)

    def delete_group(self, cn, skip_event_callback=False):
        return self.delete(self.assemble_group_dn(cn), event=self.EVENT_ON_GROUP_DELETED, skip_event_callback=skip_event_callback)

    def delete_host(self, cn, skip_event_callback=False):
        dn = self.assemble_host_dn(cn)
        result = self.delete(dn, event=self.EVENT_ON_HOST_DELETED, skip_event_callback=skip_event_callback)
        host_groups = self.search_host_groups('(uniqueMember=%s)' % self.assemble_host_dn(cn), attributes=['cn', 'uniqueMember'])
        for host_group in host_groups:
            _cn = host_group.get('cn')
            host_group.get('uniqueMember').remove(dn)
            self.modify_host_group(_cn, list(map(lambda x: self.extract_cn(x), host_group.get('uniqueMember'))), skip_event_callback=True)
        return  result

    def delete_host_group(self, cn, skip_event_callback=False):
        return self.delete(self.assemble_host_group_dn(cn), event=self.EVENT_ON_HOSTGROUP_DELETED, skip_event_callback=skip_event_callback)

    def delete_command(self, cn, skip_event_callback=False):
        dn = self.assemble_command_dn(cn)
        result = self.delete(dn, event=self.EVENT_ON_COMMAND_DELETED, skip_event_callback=skip_event_callback)
        command_groups = self.search_command_groups('(uniqueMember=%s)' % self.assemble_command_dn(cn), attributes=['cn', 'uniqueMember'])
        for command_group in command_groups:
            _cn = command_group.get('cn')
            command_group.get('uniqueMember').remove(dn)
            self.modify_command_group(_cn, list(map(lambda x: self.extract_cn(x), command_group.get('uniqueMember'))))
        return result

    def delete_command_group(self, cn, skip_event_callback=False):
        return self.delete(self.assemble_command_group_dn(cn), event=self.EVENT_ON_COMMANDGROUP_DELETED, skip_event_callback=skip_event_callback)

    def delete_service(self, cn, skip_event_callback=False):
        return self.delete(self.assemble_service_dn(cn), event=self.EVENT_ON_SERVICE_DELETED, skip_event_callback=skip_event_callback)

    def reset_password(self, cn, new_password=None, shadow_last_change=None):
        hashed_password = hashed(HASHED_SALTED_SHA, new_password)
        self.connection.modify(
            self.assemble_user_dn(cn),
            {
                'userPassword': [(MODIFY_REPLACE, [hashed_password])],
                'shadowLastChange': [(MODIFY_REPLACE, shadow_last_change if shadow_last_change is not None else self._epoch_days())],
            }
        )
Exemple #28
0
class LDAP(object):
    """ldap
    """

    _server_name = '192.168.10.98'
    _port = 389
    _user = '******'
    _password = '******'

    def __init__(self):
        self.s = Server(host=self._server_name,
                        port=self._port,
                        use_ssl=False,
                        get_info='ALL')
        self.c = Connection(self.s, user=self._user, password=self._password)
        if not self.c.bind():
            raise LDAPBindError('bind() error')

    def unbind(self):
        return self.c.unbind()

    def set_password(self):
        """随机生成10为的密码
        """
        return gen_password(10)

    def _get_current_uid(self):
        """获取ou=people里面最大的uidnumber
        为后面的添加提供uidNumber
        """
        search_base = 'ou=Yuanli,dc=chuangyu,dc=com'
        search_filter = '(objectClass=*)'
        attributes = ['uidNumber']

        result = self.c.search(search_base=search_base,
                               search_scope='LEVEL',
                               search_filter=search_filter,
                               attributes=attributes)
        if not result:
            raise LDAPExceptionError('获取people最大uid失败!')
        try:
            return max([x['attributes']['uidNumber'] for x in self.c.response])
        except:
            return 0

    def add_people_ou(self, uid, gid, userPassword=None):
        """给ou=people添加记录
        可以提供密码,如果不提供,随机生成一个
        """
        dn = 'uid={},ou=Yuanli,dc=chuangyu,dc=com'.format(uid)
        object_class = ['account', 'posixAccount', 'top']
        uidNumber = str(self._get_current_uid() + 1)
        homeDirectory = '{}@chuangyunet.com'.format(uid)
        if userPassword is None:
            userPassword = self.set_password()
        attrs = {
            'cn': uid,
            'gidNumber': str(gid),
            'homeDirectory': homeDirectory,
            'uid': uid,
            'uidNumber': uidNumber,
            'userPassword': userPassword
        }

        return self.c.add(dn, object_class, attrs)

    def get_group_dn_by_gid(self, gid):
        """通过gid来找到ou=group的dn名称
        """
        search_base = 'ou=Yuanli,dc=chuangyu,dc=com'
        search_scope = 'SUBTREE'
        search_filter = '(objectClass=*)'
        attributes = ['gidNumber']

        result = self.c.search(search_base=search_base,
                               search_scope=search_scope,
                               search_filter=search_filter,
                               attributes=attributes)

        if not result:
            search_base = 'ou=People,dc=chuangyu,dc=com'
            result = self.c.search(search_base=search_base,
                                   search_scope=search_scope,
                                   search_filter=search_filter,
                                   attributes=attributes)
            if not result:
                msg = '查找gid{} ou=group的dn失败'.format(gid)
                raise LDAPExceptionError(msg)

        for x in self.c.response:
            gidNumber = x['attributes']['gidNumber']
            if gidNumber and gidNumber == gid:
                return x['dn']
        else:
            msg = '查找gid{} ou=group的dn失败'.format(gid)
            raise LDAPExceptionError(msg)

    def get_user_gid(self, dn):
        """获取一个dn也就是ou=Yuanli的用户的gid
        """
        search_scope = 'SUBTREE'
        search_filter = '(objectClass=*)'
        attributes = ['gidNumber']

        result = self.c.search(search_base=dn,
                               search_scope=search_scope,
                               search_filter=search_filter,
                               attributes=attributes)
        if not result:
            raise LDAPExceptionError('该账号不存在'.format(dn))

        response = self.c.response
        if response:
            return response[0]['attributes']['gidNumber']
        else:
            raise LDAPExceptionError('记录{}没有gidNumber'.format(dn))

    def add_group_ou(self, gid, uid):
        """给ou=group添加记录
        先根据gid找到dn
        然后在添加uid
        uid接收list和字符串的格式
        """
        if isinstance(uid, list):
            list_uid = uid
        else:
            list_uid = [uid]
        dn = self.get_group_dn_by_gid(gid)
        modify_attr = {'memberUid': ('MODIFY_ADD', list_uid)}

        return self.c.modify(dn, modify_attr)

    def delete_people_ou(self, uid):
        """删除ou=people的dn
        """
        dn = 'uid={},ou=People,dc=chuangyu,dc=com'.format(uid)
        return self.c.delete(dn)

    def delete_yuanli_ou(self, uid):
        """删除ou=yuanli的dn
        """
        dn = 'uid={},ou=Yuanli,dc=chuangyu,dc=com'.format(uid)
        return self.c.delete(dn)

    def delete_group_ou(self, gid, uid, dn=None):
        """删除ou=group里面的某个记录
        如果有dn,则不用根据gid查找
        不然,需要先根据gid查找出dn

        uid接收list或者单个字符串, 最后全部转化为list形式
        """
        if isinstance(uid, list):
            list_uid = uid
        else:
            list_uid = [uid]

        modify_attr = {'memberUid': ('MODIFY_DELETE', list_uid)}
        if dn is not None:
            dn = dn
        else:
            dn = self.get_group_dn_by_gid(gid)

        return self.c.modify(dn, modify_attr)

    def change_user_password(self, uid, new_password):
        """修改用户密码"""
        dn = 'uid={},ou=Yuanli,dc=chuangyu,dc=com'.format(uid)
        result = self.c.modify(
            dn, {'userPassword': [(MODIFY_REPLACE, [new_password])]})
        if not result:
            dn = 'uid={},ou=People,dc=chuangyu,dc=com'.format(uid)
            result = self.c.modify(
                dn, {'userPassword': [(MODIFY_REPLACE, [new_password])]})
        return result
Exemple #29
0
class Ldap:
    def __init__(self):
        self.ldap_server = Server(
            app.config["LDAP_CONFIG"].get('host'),
            get_info=ALL,
            connect_timeout=20
        )
        self.connection = Connection(
            self.ldap_server,
            user=app.config["LDAP_CONFIG"].get('user'),
            password=app.config["LDAP_CONFIG"].get('password'),
            auto_bind=True
        )

    def search(self, search_dn, search_filter, attributes):
        """
        Ldap 服务器数据查询接口
        :param search_dn:
        :param search_filter:
        :param attributes:
        :return:
        """
        self.connection.search(search_dn, search_filter, attributes=attributes)
        response = []
        for i in self.connection.entries:
            data = json.loads(i.entry_to_json())
            response.append(data["attributes"])
        self.connection.unbind()
        return response

    @staticmethod
    def auth(user_name, password):
        """
        Ldap 用户密码认证接口
        :param user_name:
        :param password:
        :return:
        """
        ldap_user_dn = "uid=" + user_name + "," + app.config["LDAP_CONFIG"].get('people_dn')
        ldap_server = Server(app.config["LDAP_CONFIG"].get('host'), get_info=ALL)
        ldap_connect = Connection(ldap_server, user=ldap_user_dn, password=password)
        return ldap_connect.bind()

    def add(self, dn, _class, attributes):
        """
        LDAP 添加数据
        :param dn: 唯一DN
        :param _class: objectClass 对象列表
        :param attributes: json格式数据
        :return: 布尔值
        """
        try:
            self.connection.add(dn, _class, attributes)
            return True
        except Exception as Error:
            logging.error(Error)
            return False

    def delete(self, dn):
        """
        LDAP 删除数据
        :param dn: 唯一DN
        :return:
        """
        try:
            self.connection.delete(dn)
            return True
        except Exception as Error:
            logging.error(Error)
            return False

    def modify(self, dn, data):
        """
        LDAP 数据DN数据,可新增/删除/修改
        :param dn: 唯一DN
        :param data: json格式数据   {'host': [(MODIFY_DELETE, [host])]} ; MODIFY_DELETE/MODIFY_ADD/MODIFY_REPLACE
        :return:
        """
        try:
            self.connection.modify(dn, data)
            return True
        except Exception as Error:
            logging.error(Error)
            return False
Exemple #30
0
# -*- coding: utf-8 -*-
from ldap3 import Connection, Server, ALL, MODIFY_REPLACE

# define the server

s = Server('172.30.1.197', port=636, use_ssl=True, get_info=ALL)

admin_username = raw_input('Enter Admin Username....')
admin_password = raw_input('Enter Admin Password....')

#admin_username = '******'
#admin_password = '******'

# define the connection

c = Connection(s, user=admin_username, password=admin_password)

c.bind()

c.start_tls()

if not c.bind():
    print 'Admin username/password Wrong'
else:
    username = raw_input('Enter Username to delete...')
    dn = 'cn= %s,ou=Police,dc=naanal,dc=local' % username
    c.delete(dn)
    print c.result

    c.unbind()
class Test(unittest.TestCase):
    def setUp(self):
        self.connection = Connection(server=None, client_strategy=LDIF)
        self.connection.open()

    def tearDown(self):
        self.connection.unbind()
        self.assertFalse(self.connection.bound)

    def test_add_request_to_ldif(self):
        controls = list()
        controls.append(('2.16.840.1.113719.1.27.103.7', True, 'givenName'))
        controls.append(('2.16.840.1.113719.1.27.103.7', False, 'sn'))
        if str != bytes:  # python3
            controls.append(('2.16.840.1.113719.1.27.103.7', False, bytearray('\u00e0\u00e0', encoding='UTF-8')))
        else:
            controls.append(('2.16.840.1.113719.1.27.103.7', False, bytearray(unicode('\xe0\xe0', encoding='latin1'), encoding='UTF-8')))  # for python2 compatability
        controls.append(('2.16.840.1.113719.1.27.103.7', False, 'trailingspace '))
        self.connection.add(generate_dn(test_base, testcase_id, 'ldif-change-1'), 'iNetOrgPerson', {'objectClass': 'iNetOrgPerson', 'sn': 'ldif-change-1', test_name_attr: 'ldif-change-1'}, controls=controls)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-1,' + test_base in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 true: givenName' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false: sn' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false:: w6DDoA==' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false:: dHJhaWxpbmdzcGFjZSA=' in response)
        self.assertTrue('changetype: add' in response)
        self.assertTrue('objectClass: iNetOrgPerson' in response)
        self.assertTrue('sn: ldif-change-1' in response)
        self.assertTrue(test_name_attr + ': ldif-change-1' in response)

    def test_delete_request_to_ldif(self):
        self.connection.strategy.order = dict(delRequest=['dn:', 'changetype', 'vers'])
        self.connection.delete(generate_dn(test_base, testcase_id, 'ldif-change-2'))
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-2,' + test_base in response)
        self.assertTrue('changetype: delete' in response)

    def test_modify_dn_request_to_ldif(self):
        result = self.connection.modify_dn(generate_dn(test_base, testcase_id, 'ldif-change-3'), test_name_attr + '=' + testcase_id + 'ldif-change-4,' + test_base)
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-3,' + test_base in response)
        self.assertTrue('changetype: moddn' in response)
        self.assertTrue('newrdn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-4,' + test_base in response)
        self.assertTrue('deleteoldrdn: 1' in response)

    def test_move_dn_request_to_ldif(self):
        result = self.connection.modify_dn(generate_dn(test_base, testcase_id, 'ldif-change-5'), test_name_attr + '=' + testcase_id + 'ldif-change-5', delete_old_dn=False, new_superior=test_moved)
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-5,' + test_base in response)
        self.assertTrue('changetype: modrdn' in response)
        self.assertTrue('newrdn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-5' in response)
        self.assertTrue('deleteoldrdn: 0' in response)
        self.assertTrue('newsuperior: ' + test_moved in response)

    def test_modify_add_to_ldif(self):
        result = self.connection.modify(generate_dn(test_base, testcase_id, 'ldif-change-6'), {'givenName': (MODIFY_ADD, ['givenname-6-modified'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-6,' + test_base in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('add: givenName' in response)
        self.assertTrue('givenName: givenname-6-modified' in response)
        self.assertEqual('-', response[-1])

    def test_modify_replace_to_ldif(self):
        result = self.connection.modify(generate_dn(test_base, testcase_id, 'ldif-change-7'), {'givenName': (MODIFY_REPLACE, ['givenname-7-replaced'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-7,' + test_base in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('replace: givenName' in response)
        self.assertTrue('givenName: givenname-7-replaced' in response)
        self.assertEqual('-', response[-1])

    def test_modify_delete_to_ldif(self):
        result = self.connection.modify(generate_dn(test_base, testcase_id, 'ldif-change-8'), {'givenName': (MODIFY_DELETE, ['givenname-8-deleted'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id + 'ldif-change-8,' + test_base in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('delete: givenName' in response)
        self.assertTrue('givenName: givenname-8-deleted' in response)
        self.assertEqual('-', response[-1])

    def test_multiple_modify_to_ldif(self):
        # from rfc 2849 example
        result = self.connection.modify('cn=Paula Jensen, ou=Product Development, dc=airius, dc=com',
                                        {'postaladdress': (MODIFY_ADD, ['123 Anystreet $ Sunnyvale, CA $ 94086']),
                                         'description': (MODIFY_DELETE, []),
                                         'telephonenumber': (MODIFY_REPLACE, ['+1 408 555 1234', '+1 408 555 5678']),
                                         'facsimiletelephonenumber': (MODIFY_DELETE, ['+1 408 555 9876'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com' in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('delete: facsimiletelephonenumber' in response)
        self.assertTrue('facsimiletelephonenumber: +1 408 555 9876' in response)
        self.assertTrue('replace: telephonenumber' in response)
        self.assertTrue('telephonenumber: +1 408 555 1234' in response)
        self.assertTrue('telephonenumber: +1 408 555 5678' in response)
        self.assertTrue('add: postaladdress' in response)
        self.assertTrue('postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086' in response)
        self.assertTrue('delete: description' in response)
        self.assertEqual('-', response[-1])
Exemple #32
0
    # If clients wants to remove an entry
    elif operation == '3' or operation == 'r':
        to_delete = input("Which user would you like to remove: > ")
        sure = input("Are you sure you want to delete " +to_delete+"? y/n : ")
        y_or_n = False
        # Just a little security to be sure what to delete
        while not y_or_n:
            if (sure == 'y' or sure == 'n'):
                y_or_n = True
            else:
                sure = input("type y or n : > ")
        if sure == 'n':
            pass
        else:
            conn.delete('cn='+to_delete+', ou=students, dc=security, dc=ch')
            print(conn.result)

    # If client wants to modify an entry
    elif operation == '4' or operation == 'm':
        to_modify, attr_to_modify, val_to_modify = [], [], []
        str = 'y'
        to_modify.append(input("Which user would you like to modify? > "))
        print("We will get the attributes and values you want to modify, once you are done, type \'n\'.")

        while str != 'n':
            attr_to_modify.append(input("Attribute to modify: > "))
            val_to_modify.append(input("Value to modify: > "))
            str = input("Continue? y/n > ")

        for i in range(len(attr_to_modify)):
Exemple #33
0
class LdapFunctions:
    
    def __init__(self):

        self.server_ip = '54.36.182.216'
        self.ldap_username = '******'
        self.ldap_password = '******'
        self.ldap_server = Server(self.server_ip, get_info=ALL)
        self.conn = Connection(self.server_ip, self.ldap_username, self.ldap_password, auto_bind=True)

    def connect(self):
        self.conn.bind()

    def disconnect(self):
        self.conn.unbind()
    
    def get_users(self):
        try:
            self.connect()
            self.conn.search('ou=People,dc=insat,dc=chat,dc=com', '(&(objectclass=inetOrgPerson)(!(uid=0001)))', attributes=['*'])
            users = []
            for entry in self.conn.entries:
                user = json.loads(entry.entry_to_json())
                if('userSMIMECertificate' in user['attributes']):
                    L = ast.literal_eval(user['attributes']['userSMIMECertificate'][0])
                    user['attributes']['userCertificate'] = str(np.array(L, dtype='int8').tobytes())
                users.append(user)

            self.disconnect()
            return users
        except ldap3.LDAPError:
            self.conn.unbind()
            return 'authentication error'

    def get_user(self, username=None):
        if(username == None):
            return {}
        try:
            self.connect()
            #attributes=['cn', 'sn']
            self.conn.search('ou=People,dc=insat,dc=chat,dc=com', '(&(objectclass=inetOrgPerson)(sn=' + username + ')(!(uid=0001)))', attributes=['displayName', 'uid', 'givenName', 'userPKCS12', 'sn', 'userSMIMECertificate'])
            if(not self.conn.entries):
                return "User doesn't exist"
            else:
                user = json.loads(self.conn.entries[0].entry_to_json())
                if('userSMIMECertificate' in user['attributes']):
                    L = ast.literal_eval(user['attributes']['userSMIMECertificate'][0])
                    user['attributes']['userCertificate'] = str(np.array(L, dtype='int8').tobytes())

            self.disconnect()
            return user
        except ldap3.LDAPError:
            self.conn.unbind()
            return 'authentication error'

    def login(self, username, password):
        try:
            self.connect()
            m = hashlib.sha256(str(password).encode('utf-8'))
            hashed_pass = m.hexdigest()
            self.conn.search('ou=People,dc=insat,dc=chat,dc=com', '(&(objectclass=inetOrgPerson)(sn=' + username + ')(userPassword='******'))', attributes=['sn', 'displayName', 'givenName', 'uid', 'userPKCS12', 'userSMIMECertificate'])
            if(not self.conn.entries):
                return None
            else:
                user = json.loads(self.conn.entries[0].entry_to_json())
                print(user)
                if('userSMIMECertificate' in user['attributes']):
                    L = ast.literal_eval(user['attributes']['userSMIMECertificate'][0])
                    user['attributes']['userCertificate'] = str(np.array(L, dtype='int8').tobytes())

            self.disconnect()
            return user
        except ldap3.LDAPError:
            self.conn.unbind()
            return 'authentication error'

    def add_user(self):
        try:
            self.connect()
            user = json.loads(request.data)
            #print(user)

            #before adding i need to select by username to make sure it's unique
            username = user['sn']
            self.conn.search('ou=People,dc=insat,dc=chat,dc=com', '(&(objectclass=inetOrgPerson)(sn=' + username + '))', attributes=['sn'])
            if(self.conn.entries):
                self.conn.unbind()
                return "User already exists"
            else:
                m = hashlib.sha256(str(user['userPassword']).encode('utf-8'))
                hashed_pass = m.hexdigest()
                res = self.conn.add(
                    'cn=' + user['sn'] + ',ou=People,dc=insat,dc=chat,dc=com',
                    attributes={
                        "objectClass": "inetOrgPerson",
                        "sn":  user['sn'],
                        "uid": user['uid'],
                        "givenName": user['givenName'],
                        "displayName": user['displayName'],
                        "userPassword": hashed_pass,
                        "userPKCS12": user['userPKCS12'],
                        #"userCertificate": 
                    },
                )

                self.conn.unbind()
                if(res):
                    return 'User added successfully'
                else:
                    return 'User already exists'

        except ldap3.LDAPError:
            self.conn.unbind()
            return 'Authentication error'

    def modify_user(self, username):
        try:
            self.connect()
            user = json.loads(request.data)
            #print(user)
            m = hashlib.sha256(str(user['userPassword']).encode('utf-8'))
            hashed_pass = m.hexdigest()
            
            edits = {}
            if 'displayName' in user:
                edits['displayName'] = [(MODIFY_REPLACE, user['displayName'])]
            if 'givenName' in user:
                edits['givenName'] = [(MODIFY_REPLACE, user['givenName'])]
            if 'uid' in user:
                edits['uid'] = [(MODIFY_REPLACE, user['uid'])]
            if 'userPKCS12' in user:
                edits['userPKCS12'] = [(MODIFY_REPLACE, user['userPKCS12'])]
            if 'userPassword' in user:
                edits['userPassword'] = [(MODIFY_REPLACE, hashed_pass)]

            res = self.conn.modify(
                dn='cn=' + username + ',ou=People,dc=insat,dc=chat,dc=com',
                changes=edits,
            )

            self.conn.unbind()
            if(res):
                return 'User edited succefully'
            else:
                return 'An error has occured'

        except ldap3.LDAPError:
            self.conn.unbind()
            return 'Authentication error'

    def delete_user(self, username=None):
        if(username == None):
            return 'An error has occured'
        try:
            self.connect()
            res = self.conn.delete('cn=' + username + ',ou=People,dc=insat,dc=chat,dc=com')
            self.conn.unbind()
            if(res):
                return 'User deleted succefully'
            else:
                return 'An error has occured'
        except ldap3.LDAPError:
            self.conn.unbind()
            return 'Authentication error'
Exemple #34
0
class Test(unittest.TestCase):
    def setUp(self):
        global testcase_id
        testcase_id = random_id()
        self.connection = Connection(server=None, client_strategy=LDIF)
        self.connection.open()

    def tearDown(self):
        self.connection.unbind()
        self.assertFalse(self.connection.bound)

    def test_add_request_to_ldif(self):
        controls = list()
        controls.append(('2.16.840.1.113719.1.27.103.7', True, 'givenName'))
        controls.append(('2.16.840.1.113719.1.27.103.7', False, 'sn'))
        if str != bytes:  # python3
            controls.append(('2.16.840.1.113719.1.27.103.7', False,
                             bytearray('\u00e0\u00e0', encoding='UTF-8')))
        else:
            controls.append(
                ('2.16.840.1.113719.1.27.103.7', False,
                 bytearray(unicode('\xe0\xe0', encoding='latin1'),
                           encoding='UTF-8')))  # for python2 compatibility
        controls.append(
            ('2.16.840.1.113719.1.27.103.7', False, 'trailingspace '))
        self.connection.add(generate_dn(test_base, testcase_id,
                                        'ldif-change-1'),
                            'inetOrgPerson', {
                                'objectClass': 'inetOrgPerson',
                                'sn': 'ldif-change-1',
                                test_name_attr: 'ldif-change-1'
                            },
                            controls=controls)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: ' + test_name_attr + '=' + testcase_id +
                        'ldif-change-1,' + test_base in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 true: givenName'
                        in response)
        self.assertTrue(
            'control: 2.16.840.1.113719.1.27.103.7 false: sn' in response)
        self.assertTrue(
            'control: 2.16.840.1.113719.1.27.103.7 false:: w6DDoA==' in
            response)
        self.assertTrue(
            'control: 2.16.840.1.113719.1.27.103.7 false:: dHJhaWxpbmdzcGFjZSA='
            in response)
        self.assertTrue('changetype: add' in response)
        self.assertTrue('objectClass: inetOrgPerson' in response)
        self.assertTrue('sn: ldif-change-1' in response)
        self.assertTrue(test_name_attr + ': ldif-change-1' in response)

    def test_delete_request_to_ldif(self):
        self.connection.strategy.order = dict(
            delRequest=['dn:', 'changetype', 'vers'])
        self.connection.delete(
            generate_dn(test_base, testcase_id, 'ldif-change-2'))
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-2,' + test_base in response)
        self.assertTrue('changetype:delete' in response)

    def test_modify_dn_request_to_ldif(self):
        result = self.connection.modify_dn(
            generate_dn(test_base, testcase_id, 'ldif-change-3'),
            test_name_attr + '=' + testcase_id + 'ldif-change-4,' + test_base)
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-3,' + test_base in response)
        self.assertTrue('changetype:moddn' in response)
        self.assertTrue('newrdn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-4,' + test_base in response)
        self.assertTrue('deleteoldrdn:1' in response)

    def test_move_dn_request_to_ldif(self):
        result = self.connection.modify_dn(
            generate_dn(test_base, testcase_id, 'ldif-change-5'),
            test_name_attr + '=' + testcase_id + 'ldif-change-5',
            delete_old_dn=False,
            new_superior=test_moved)
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-5,' + test_base in response)
        self.assertTrue('changetype:modrdn' in response)
        self.assertTrue('newrdn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-5' in response)
        self.assertTrue('deleteoldrdn:0' in response)
        self.assertTrue('newsuperior:' + test_moved in response)

    def test_modify_add_to_ldif(self):
        result = self.connection.modify(
            generate_dn(test_base, testcase_id, 'ldif-change-6'),
            {'givenName': (MODIFY_ADD, ['givenname-6-modified'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-6,' + test_base in response)
        self.assertTrue('changetype:modify' in response)
        self.assertTrue('add:givenName' in response)
        self.assertTrue('givenName:givenname-6-modified' in response)
        self.assertEqual('-', response[-1])

    def test_modify_replace_to_ldif(self):
        result = self.connection.modify(
            generate_dn(test_base, testcase_id, 'ldif-change-7'),
            {'givenName': (MODIFY_REPLACE, ['givenname-7-replaced'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-7,' + test_base in response)
        self.assertTrue('changetype:modify' in response)
        self.assertTrue('replace:givenName' in response)
        self.assertTrue('givenName:givenname-7-replaced' in response)
        self.assertEqual('-', response[-1])

    def test_modify_delete_to_ldif(self):
        result = self.connection.modify(
            generate_dn(test_base, testcase_id, 'ldif-change-8'),
            {'givenName': (MODIFY_DELETE, ['givenname-8-deleted'])})
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response.replace('\r\n',
                                                    '').replace(' ', '')
        self.assertTrue('version:1' in response)
        self.assertTrue('dn:' + test_name_attr + '=' + testcase_id +
                        'ldif-change-8,' + test_base in response)
        self.assertTrue('changetype:modify' in response)
        self.assertTrue('delete:givenName' in response)
        self.assertTrue('givenName:givenname-8-deleted' in response)
        self.assertEqual('-', response[-1])

    def test_multiple_modify_to_ldif(self):
        # from rfc 2849 example
        result = self.connection.modify(
            'cn=Paula Jensen,ou=Product Development,dc=airius,dc=com', {
                'postaladdress':
                (MODIFY_ADD, ['123 Anystreet $ Sunnyvale, CA $ 94086']),
                'description': (MODIFY_DELETE, []),
                'telephonenumber':
                (MODIFY_REPLACE, ['+1 408 555 1234', '+1 408 555 5678']),
                'facsimiletelephonenumber':
                (MODIFY_DELETE, ['+1 408 555 9876'])
            })
        if isinstance(result, int):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue(
            'dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com' in
            response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('delete: facsimiletelephonenumber' in response)
        self.assertTrue(
            'facsimiletelephonenumber: +1 408 555 9876' in response)
        self.assertTrue('replace: telephonenumber' in response)
        self.assertTrue('telephonenumber: +1 408 555 1234' in response)
        self.assertTrue('telephonenumber: +1 408 555 5678' in response)
        self.assertTrue('add: postaladdress' in response)
        self.assertTrue(
            'postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086' in response)
        self.assertTrue('delete: description' in response)
        self.assertEqual('-', response[-1])
Exemple #35
0
class LdapOLC(object):
    def __init__(self, addr, binddn, passwd):
        self.addr = addr
        self.binddn = binddn
        self.passwd = passwd
        self.server = None
        self.conn = None
        self.hostname = get_host_port(addr)[0]

    def connect(self):
        logger.debug("Making Ldap Connection")
        self.server = Server(self.addr, use_ssl=True)
        self.conn = Connection(self.server,
                               user=self.binddn,
                               password=self.passwd)
        return self.conn.bind()

    def loadModules(self, *modules):
        """If modules are loaded, returns status, If modules are already loaded returns -1"""
        self.conn.search(search_base='cn=module{0},cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcModuleLoad"])

        addList = list(modules)

        if self.conn.response:
            for a in self.conn.response[0]['attributes']['olcModuleLoad']:
                r = re.split("{\d+}", a)
                if len(r) == 1:
                    m = r[0]
                else:
                    m = r[1]
                mn = m.split('.')
                if mn[0] in addList:
                    addList.remove(mn[0])

        if addList:

            return self.conn.modify('cn=module{0},cn=config',
                                    {'olcModuleLoad': [MODIFY_ADD, addList]})

        return -1

    def checkAccesslogDBEntry(self):
        return self.conn.search(search_base='cn=config',
                                search_filter='(olcSuffix=cn=accesslog)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def accesslogDBEntry(self,
                         replicator_dn,
                         log_dir="/opt/gluu/data/accesslog"):

        attributes = {
            'objectClass': ['olcDatabaseConfig', 'olcMdbConfig'],
            'olcDatabase':
            '{2}mdb',
            'olcDbDirectory':
            log_dir,
            'OlcDbMaxSize':
            1073741824,
            'olcSuffix':
            'cn=accesslog',
            'olcRootDN':
            'cn=admin, cn=accesslog',
            'olcRootPW':
            ldap_encode(self.passwd),
            'olcDbIndex': [
                'default eq',
                'objectClass,entryCSN,entryUUID,reqEnd,reqResult,reqStart'
            ],
            'olcLimits':
            'dn.exact="{0}" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited'
            .format(replicator_dn),
        }

        if not self.checkAccesslogDBEntry():
            return self.conn.add('olcDatabase={2}mdb,cn=config',
                                 attributes=attributes)

    def checkSyncprovOverlaysDB1(self):
        return self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                                search_filter='(olcOverlay=syncprov)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def syncprovOverlaysDB1(self):
        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcSyncProvConfig'],
            'olcOverlay': 'syncprov',
            # 'olcSpNoPresent': 'TRUE', ???
            'olcSpReloadHint': 'TRUE',
            'olcSpCheckPoint': '100 10',
            'olcSpSessionlog': '10000',
        }
        if not self.checkSyncprovOverlaysDB1():
            self.conn.add('olcOverlay=syncprov,olcDatabase={1}mdb,cn=config',
                          attributes=attributes)
            if self.conn.result['description'] == 'success':
                return True

    def checkSyncprovOverlaysDB2(self):
        return self.conn.search(search_base='olcDatabase={2}mdb,cn=config',
                                search_filter='(olcOverlay=syncprov)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def syncprovOverlaysDB2(self):
        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcSyncProvConfig'],
            # 'structuralObjectClass': ['olcSyncProvConfig'],
            'olcOverlay': 'syncprov',
            'olcSpNoPresent': 'TRUE',
            'olcSpReloadHint': 'TRUE',
            # 'olcSpCheckPoint': '100 10',
            # 'olcSpSessionlog': '10000',
            # 'olcLimits': 'dn.exact="cn=directory manager,o=gluu" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited',
        }
        if not self.checkSyncprovOverlaysDB2():
            self.conn.add('olcOverlay=syncprov,olcDatabase={2}mdb,cn=config',
                          attributes=attributes)

            if self.conn.result['description'] == 'success':
                return True

    def checkServerID(self):
        return self.conn.search(search_base='cn=config',
                                search_filter='(objectClass=*)',
                                search_scope=BASE,
                                attributes=["olcServerID"])

    def setServerID(self, sid):

        mod_type = MODIFY_ADD
        self.conn.search(search_base='cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcServerID"])

        if self.checkServerID():
            if self.conn.response[0]['attributes']['olcServerID']:
                mod_type = MODIFY_REPLACE

        return self.conn.modify('cn=config',
                                {'olcServerID': [mod_type, str(sid)]})

    def setDBIndexes(self):
        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcDbIndex"])
        addList = ["entryCSN eq", "entryUUID eq"]

        if self.conn.response:
            for idx in self.conn.response[0]['attributes']['olcDbIndex']:
                if idx in addList:
                    addList.remove(idx)

        return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                {'olcDbIndex': [MODIFY_ADD, addList]})

    def checkAccesslogPurge(self):
        return self.conn.search(
            search_base='cn=config',
            search_filter='(objectClass=olcAccessLogConfig)',
            search_scope=SUBTREE,
            attributes=["olcAccessLogPurge"])

    def accesslogPurge(self):
        attributes = {
            'objectClass': ['olcOverlayConfig', 'olcAccessLogConfig'],
            'olcOverlay': 'accesslog',
            'olcAccessLogDB': 'cn=accesslog',
            'olcAccessLogOps': 'writes',
            'olcAccessLogSuccess': 'TRUE',
            'olcAccessLogPurge': '07+00:00 01+00:00',
        }
        if not self.checkAccesslogPurge():
            return self.conn.add(
                'olcOverlay=accesslog,olcDatabase={1}mdb,cn=config',
                attributes=attributes)

    def removeMirrorMode(self):
        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcMirrorMode"])

        if not self.conn.response:
            return

        if self.conn.response[0]['attributes']['olcMirrorMode']:
            return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                    {"olcMirrorMode": [MODIFY_REPLACE, []]})

    def checkMirroMode(self):
        r = self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                             search_filter='(objectClass=*)',
                             search_scope=BASE,
                             attributes=["olcMirrorMode"])
        if r:
            if self.conn.response[0]['attributes']:
                if self.conn.response[0]['attributes']['olcMirrorMode']:
                    return self.conn.response[0]['attributes']['olcMirrorMode']

        return False

    def makeMirroMode(self):
        return self.conn.modify('olcDatabase={1}mdb,cn=config',
                                {"olcMirrorMode": [MODIFY_ADD, ["TRUE"]]})

    def removeProvider(self, raddr):
        rmMirrorMode = False

        if len(self.getProviders()) <= 1:
            rmMirrorMode = True

        if not self.conn.response:
            return -1

        for pr in self.conn.response:
            if pr["attributes"]["olcSyncrepl"]:
                for pri in pr["attributes"]["olcSyncrepl"]:
                    for l in pri.split():
                        ls = l.split('=')
                        if ls[0] == 'provider':
                            if ls[1] == raddr:
                                baseDn = pr['dn']
                                r = self.conn.modify(
                                    baseDn,
                                    {'olcSyncrepl': [MODIFY_DELETE, [pri]]})
                                if r:
                                    if rmMirrorMode:
                                        self.removeMirrorMode()
                                return r

    def add_provider(self, rid, raddr, rbinddn, rcredentials):
        ridText = (
            'rid={0} provider={1} bindmethod=simple binddn="{2}" '
            'tls_reqcert=never credentials={3} searchbase="o=gluu" '
            'logbase="cn=accesslog" '
            #'filter=(&(objectClass=*)(!(ou:dn:=appliances))) '
            'logfilter="(&(objectClass=auditWriteObject)(reqResult=0))" '
            'schemachecking=on type=refreshAndPersist retry="60 +" '
            'syncdata=accesslog sizeLimit=unlimited '
            'timelimit=unlimited'.format(rid, raddr, rbinddn, rcredentials))

        self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                         search_filter='(objectClass=*)',
                         search_scope=BASE,
                         attributes=["olcSyncRepl"])

        # delete the entry if a syncrepl config exists for the same rid
        entry = self.conn.entries[0]
        for rep in entry["olcSyncRepl"]:
            if 'rid={0}'.format(rid) in rep:
                lmod = {"olcSyncRepl": [(MODIFY_DELETE, [rep])]}
                self.conn.modify('olcDatabase={1}mdb,cn=config', lmod)
                break

        mod = {"olcSyncRepl": [(MODIFY_ADD, [ridText])]}

        return self.conn.modify('olcDatabase={1}mdb,cn=config', mod)

    def checkAccesslogDB(self):
        return self.conn.search(search_base='cn=config',
                                search_filter='(olcSuffix=cn=accesslog)',
                                search_scope=SUBTREE,
                                attributes=["*"])

    def addTestUser(self, cn, sn, mail):
        self.checkBaseDN()
        self.checkTestUserBase()
        uid = '{0}@{1}'.format(time.time(), self.hostname)
        dn = "uid={0},ou=testusers,o=gluu".format(uid)
        return self.conn.add(dn,
                             attributes={
                                 'objectClass': ['top', 'inetOrgPerson'],
                                 "cn": cn,
                                 'mail': mail,
                                 'sn': sn,
                                 'title': 'gluuClusterMgrTestUser',
                                 'uid': uid
                             })

    def checkTestUserBase(self):
        if not self.conn.search(search_base='ou=testusers,o=gluu',
                                search_filter='(objectClass=inetOrgPerson)',
                                search_scope=BASE,
                                attributes='*'):
            self.conn.add('ou=testusers,o=gluu',
                          attributes={
                              'objectClass': ['top', 'organizationalUnit'],
                              'ou': 'testusers',
                          })

    def searchTestUsers(self):
        return self.conn.search(search_base='ou=testusers,o=gluu',
                                search_filter='(title=gluuClusterMgrTestUser)',
                                search_scope=LEVEL,
                                attributes='*')

    def delDn(self, dn):
        return self.conn.delete(dn)

    def getProviders(self):
        pDict = {}
        if self.conn.search(search_base='olcDatabase={1}mdb,cn=config',
                            search_filter='(objectClass=*)',
                            search_scope=BASE,
                            attributes=["olcSyncRepl"]):

            for pe in self.conn.response[0]['attributes']['olcSyncrepl']:
                for e in pe.split():
                    es = e.split("=")
                    if re.search('(\{\d*\})*rid', es[0]):
                        pid = es[1]
                    elif es[0] == 'provider':
                        host, port = get_host_port(es[1])
                        dkey = host
                        if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",
                                    host):
                            dkey = get_hostname_by_ip(host)

                pDict[dkey] = (pid, port, host)

        return pDict

    def getMMRStatus(self):
        retDict = {}
        retDict["server_id"] = None
        if self.checkServerID():
            if self.conn.response[0]['attributes']['olcServerID']:
                retDict["server_id"] = self.conn.response[0]['attributes'][
                    'olcServerID'][0]

        retDict["overlaysDB1"] = self.checkSyncprovOverlaysDB1()
        retDict["overlaysDB2"] = self.checkSyncprovOverlaysDB2()
        retDict["mirrorMode"] = self.checkMirroMode()
        retDict["accesslogDB"] = self.checkAccesslogDBEntry()
        retDict["accesslogPurge"] = self.checkAccesslogPurge()
        retDict["providers"] = self.getProviders()

        return retDict

    def getMainDbDN(self):
        if self.conn.search(
                search_base="cn=config",
                search_scope=LEVEL,
                search_filter="(olcDbDirectory=/opt/gluu/data/main_db)",
                attributes='*'):
            if self.conn.response:
                return self.conn.response[0]['dn']

    def setLimitOnMainDb(self, replicator_dn):
        main_db_dn = self.getMainDbDN()
        return self.conn.modify(
            main_db_dn, {
                'olcLimits': [
                    MODIFY_ADD,
                    'dn.exact="{0}" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited'
                    .format(replicator_dn)
                ]
            })

    def addReplicatorUser(self, replicator_dn, passwd):
        self.checkBaseDN()
        enc_passwd = ldap_encode(passwd)
        self.conn.search(replicator_dn,
                         search_filter='(objectClass=*)',
                         search_scope=BASE)

        if len(self.conn.response):  # user dn already exists
            return self.conn.modify(
                replicator_dn, {"userPassword": [MODIFY_REPLACE, enc_passwd]})
        else:
            m = re.search('cn=(?P<cn>[a-zA-Z][a-zA-Z ]*[a-zA-Z]),o=gluu',
                          replicator_dn)
            cn = m.group('cn')
            attributes = {
                'objectClass': ['top', 'inetOrgPerson'],
                'cn': cn,
                'sn': 'replicator',
                'uid': 'replicator',
                'userpassword': enc_passwd,
            }
            return self.conn.add(replicator_dn, attributes=attributes)

    def checkBaseDN(self):
        r = self.conn.search(search_base="o=gluu",
                             search_filter='(objectClass=top)',
                             search_scope=BASE)
        if not self.conn.search(search_base="o=gluu",
                                search_filter='(objectClass=top)',
                                search_scope=BASE):
            logger.info("Adding base DN")
            self.conn.add('o=gluu',
                          attributes={
                              'objectClass': ['organization'],
                              'o': 'gluu',
                          })

    def configureOxIDPAuthentication(self, servers):
        if self.conn.search("ou=appliances,o=gluu",
                            search_filter='(objectClass=gluuAppliance)',
                            search_scope=LEVEL,
                            attributes=["oxIDPAuthentication"]):
            r = self.conn.response
            if r:
                oxidp_s = r[0]["attributes"]["oxIDPAuthentication"][0]
                oxidp = json.loads(oxidp_s)
                config = json.loads(oxidp["config"])
                config["servers"] = servers
                oxidp["config"] = json.dumps(config)
                oxidp_s = json.dumps(oxidp)
                return self.conn.modify(
                    r[0]['dn'],
                    {"oxIDPAuthentication": [MODIFY_REPLACE, oxidp_s]})
Exemple #36
0
class LDAP:
    def __init__(self):

        self.ldap_conn = Connection(server=Server('127.0.0.1'),
                                    user=f'cn={admin_user},{ldap_suffix}',
                                    password=admin_cred,
                                    auto_bind=True)

        self.num_hits = 0
        self.num_search_hits = 0
        self.num_change_hits = 0

        self.group_dict = {}

        self.invalid_users = []
        self.invalid_group_members = {}

        self.users_dict_uid = {}
        self.users_dict_eid = {}
        self.user_eips = []
        self.user_md5s = []

        self.__user_populate()

        self.number_of_users = len(self.user_eips)

    # Private helpers

    def __user_populate(self):
        for userEntry in self.__user_search(['*']):
            dn = userEntry.entry_dn
            try:
                user_eid = userEntry.employeeNumber.value
                user_md5 = userEntry.carLicense.value
                user_uid = userEntry.uid.value

                self.users_dict_uid[user_uid] = {
                    'm5': user_md5,
                    'eid': user_eid,
                    'curr_groups': [],
                    'dn': dn
                }
                self.users_dict_eid[user_eid] = {
                    'm5': user_md5,
                    'uid': user_uid,
                    'curr_groups': [],
                    'dn': dn
                }
                self.user_eips.append(user_eid)
                self.user_md5s.append(user_md5)
            except LDAPCursorAttributeError as e:
                log("WARNING",
                    f"Invalid configured user - {userEntry.entry_dn}")
                self.invalid_users.append(userEntry)

        for g in self.get_all_groups():
            self.group_dict[g] = []

        for e in self.__group_search(['cn', 'memberUid']):
            memberUids = e.memberUid.value
            group = e.cn.value
            if memberUids == None: members = []
            elif type(memberUids) == str: members = [memberUids]
            elif type(memberUids) == list: members = memberUids
            else:
                log("CRITICAL",
                    f"Unknown MemberUid State {group} - {memberUids}")

            for user_uid in members:
                self.group_dict[group].append(user_uid)
                if user_uid not in self.users_dict_uid.keys():
                    if e.cn.value not in self.invalid_group_members.keys():
                        self.invalid_group_members[e.cn.value] = []
                    log("WARNING",
                        f"Unknown user {user_uid} in group {e.cn.value}.")
                    self.invalid_group_members[e.cn.value].append(user_uid)
                else:
                    self.users_dict_uid[user_uid]['curr_groups'].append(
                        e.cn.value)

    def __check_result(self, result, val='unknown', acceptable=[]):
        self.num_hits += 1
        if result != True and self.ldap_conn.result['result'] != 0:
            error = self.ldap_conn.result['description']
            if error in acceptable:
                log("WARNING", f"{error}: {val}")
            else:
                log("CRITICAL", f"{val} {self.ldap_conn.result}")

    def __sambaDomainName_unique(self, attribute):
        self.num_search_hits += 1
        result = self.ldap_conn.search(
            search_base=f'sambaDomainName=sambaDomain,{ldap_suffix}',
            search_filter='(objectClass=*)',
            search_scope='SUBTREE',
            attributes=[attribute])
        self.__check_result(result)
        number = self.ldap_conn.entries[0][attribute].values[0] + 1
        self.num_change_hits += 1
        result = self.ldap_conn.modify(
            dn=f'sambaDomainName=sambaDomain,{ldap_suffix}',
            changes={attribute: [(MODIFY_REPLACE, number)]})
        self.__check_result(result)
        return number

    def __group_search(self, attributes):
        self.num_search_hits += 1
        result = self.ldap_conn.search(
            search_base=f'ou=group,{ldap_suffix}',
            search_filter='(objectClass=posixGroup)',
            search_scope='SUBTREE',
            attributes=attributes)
        self.__check_result(result)
        return self.ldap_conn.entries

    def __user_search(self, attributes, uid='*'):
        self.num_search_hits += 1
        result = self.ldap_conn.search(
            search_base=f'ou=people,{ldap_suffix}',
            search_filter=f'(&(uid={uid})(objectClass=inetOrgPerson))',
            search_scope='SUBTREE',
            attributes=attributes)
        return self.ldap_conn.entries

    # Groups

    def group_create(self, group):
        dn = f'cn={group},ou=group,{ldap_suffix}'
        log("CREATE", f"create group {dn}")
        self.num_change_hits += 1
        result = self.ldap_conn.add(
            dn=dn,
            object_class=[
                'top', 'posixGroup', 'SambaGroupMapping', 'extensibleObject'
            ],
            attributes={
                'gidNumber': self.__sambaDomainName_unique('gidNumber'),
                'sambaSID': sambaSID,
                'sambaGroupType': 2
            })
        self.__check_result(result)

    def group_delete(self, group):
        dn = f'cn={group},ou=group,{ldap_suffix}'
        log(
            'DELETE',
            f"delete group {dn} which included {', '.join(self.group_dict[group]) if self.group_dict[group] != [] else 'no members.'}"
        )
        self.num_change_hits += 1
        self.__check_result(self.ldap_conn.delete(dn=dn))

    def groups_enforce(self, uid, config_groups, cfg_user_md5):
        current_groups = set(self.user_membership_preloaded(uid))
        should_be_groups = set(config_groups)
        remove = current_groups.difference(should_be_groups)
        add = should_be_groups.difference(current_groups)

        if len(add) != 0:
            log("ENFORCE",
                f"{uid:<15} {'member of':<15} {', '.join(should_be_groups)}")
            for g in add:
                self.groups_add_user(uid, g)

        if len(remove) != 0:
            log("ENFORCE",
                f"{uid:<15} {'NOT member of':<15} {', '.join(remove)}")
            for g in remove:
                self.groups_remove_user(uid, g)

        if (len(add) + len(remove)) != 0:
            self.num_change_hits += 1
            result = self.ldap_conn.modify(
                dn=f'uid={uid},ou=people,{ldap_suffix}',
                changes={'carLicense': [(MODIFY_REPLACE, cfg_user_md5)]})
            self.__check_result(
                result, f"[USER: {uid} MD5: {cfg_user_md5} ACTION: modify ]")

    def groups_add_user(self, uid, group):
        log("ADD", f"    * add {uid} to {group}")
        self.num_change_hits += 1
        result = self.ldap_conn.modify(
            dn=f'cn={group},ou=group,{ldap_suffix}',
            changes={'memberUid': [(MODIFY_ADD, uid)]})
        self.__check_result(result,
                            f"[GROUP: '{group}' USER: '******', ACTION: add]",
                            ['attributeOrValueExists'])

    def groups_remove_user(self, uid, group):
        log("REMOVE", f"    * remove {uid} from {group}")
        self.num_change_hits += 1
        result = self.ldap_conn.modify(
            dn=f'cn={group},ou=group,{ldap_suffix}',
            changes={'memberUid': [(MODIFY_DELETE, uid)]})
        self.__check_result(
            result, f"[GROUP: '{group}' USER: '******', ACTION: remove]",
            ['noSuchAttribute'])

    # Users

    def user_create(self, uid, eip, mail, m5d, init_groups):
        dn = f'uid={uid},ou=people,{ldap_suffix}'
        log("CREATE", f"create user {dn} and to {', '.join(init_groups)}")
        self.num_change_hits += 1
        result = self.ldap_conn.add(
            dn=dn,
            object_class=[
                'top', 'person', 'organizationalPerson', 'posixAccount',
                'shadowAccount', 'inetOrgPerson'
            ],
            attributes={
                'sn': "TODO",
                'homeDirectory': f"home/{uid}",
                'cn': f"TODO",
                'employeeNumber': eip,
                'uidNumber': self.__sambaDomainName_unique('uidnumber'),
                'gidNumber': 512,
                'mail': mail,
                'carLicense': m5d,
            })
        self.__check_result(result, uid,
                            ['entryAlreadyExists', 'attributeOrValueExists'])
        for g in init_groups:
            self.groups_add_user(uid, g)

    def user_rename(self, old_uid, new_uid, cfg_user_md5):
        log("RENAME", f"uid rename from {old_uid} to {new_uid}")

        self.num_change_hits += 1
        result = self.ldap_conn.modify_dn(
            dn=f'uid={old_uid},ou=people,{ldap_suffix}',
            relative_dn=f'uid={new_uid}')
        self.__check_result(result)

        for group in self.user_membership_preloaded(old_uid):
            self.groups_remove_user(old_uid, group)
            self.groups_add_user(new_uid, group)

        self.users_dict_uid[new_uid] = self.users_dict_uid[old_uid]
        del self.users_dict_uid[old_uid]

        self.num_change_hits += 1
        result = self.ldap_conn.modify(
            dn=f'uid={new_uid},ou=people,{ldap_suffix}',
            changes={'carLicense': [(MODIFY_REPLACE, cfg_user_md5)]})
        self.__check_result(
            result,
            f"[USER: {old_uid}->{new_uid} MD5: {cfg_user_md5} ACTION: modify ]"
        )

    def user_delete(self, uid, dn):
        log("DELETE", f"delete user {dn}")
        for group in self.user_membership_preloaded(uid):
            self.groups_remove_user(uid, group)
        self.num_change_hits += 1
        result = self.ldap_conn.delete(dn)
        self.__check_result(result, uid, ['noSuchAttribute'])

    def user_get_members_of_real(self, uid):
        self.num_search_hits += 1
        result = self.ldap_conn.search(
            search_base=f'{ldap_suffix}',
            search_filter=f'(&(cn=*)(memberUid={uid}))',
            attributes=['cn'])
        self.__check_result(result)
        return [g.cn.value for g in self.ldap_conn.entries]

    def user_membership_preloaded(self, uid):
        if uid not in self.users_dict_uid.keys(): return []
        return self.users_dict_uid[uid]['curr_groups']

    def get_users_employeenumber(self):
        return self.user_eips

    def get_users_m5d(self):
        return self.user_md5s

    def get_all_groups(self):
        return [e.cn.value for e in self.__group_search(['cn'])]
Exemple #37
0
    class __Ldap:
        ldap = None

        def __init__(self, settings=None):
            self.settings = settings or Settings()
            self.logger = Logger(__name__)

        def getInstance(self):
            settings = self.settings.getSettings()
            user = settings['ldap']['admin_ldap_username']
            password = settings['ldap']['admin_ldap_password']
            host = self.settings.getServiceIp('ldap')
            self.logger.debug("Connecting to " + host + " with user " + user)
            self.dn_base = settings['ldap']['ldap_cn_base']
            server = Server(host, get_info=ALL)
            self.ldapClient = Connection(server,
                                         user=user,
                                         password=password,
                                         raise_exceptions=True)
            try:
                self.ldapClient.bind()
            except ldap.LDAPSocketOpenError as e:
                self.logger.error(
                    "Could not connect to LDAP - SocketOpenError: " + str(e))
            return self

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            return self.ldapClient.unbind()

        def disconnect(self):
            self.ldapClient.unbind()

        def createUser(self, user, firstname, lastname, password, email):
            password = password.encode('utf-8')
            hashPassword = hashlib.md5()
            hashPassword.update(password)
            password = base64.b64encode(hashPassword.digest())
            dn = "cn=" + user + "," + self.dn_base
            attrs = {}
            attrs['objectClass'] = [
                'inetOrgPerson', 'person', 'top', 'organizationalPerson'
            ]
            attrs['cn'] = user
            attrs['userPassword'] = "******" + password.decode('utf-8')
            attrs['sn'] = lastname
            attrs['givenName'] = firstname
            attrs['mail'] = email
            try:
                self.ldapClient.add(dn, attributes=attrs)
                self.logger.info(self.ldapClient.result)
            except ldap.LDAPEntryAlreadyExistsResult:
                self.logger.error("Could not create user, duplicated " + user)
                return False
            except Exception as e:
                self.logger.error("Could not create user: "******"cn=" + user + "," + self.dn_base
            self.ldapClient.delete(deleteDN)
            return True

        def findUser(self, user):
            self.ldapClient.search(
                search_base=self.dn_base,
                search_filter='(&(objectClass=inetOrgPerson)(cn=' + user +
                '))',
                search_scope=SUBTREE,
                attributes=['cn'])

            usernames = []
            for result in self.ldapClient.response:
                cn = result['attributes']['cn'][0]
                if cn:
                    usernames.append(cn)

            return usernames
class Test(unittest.TestCase):
    def setUp(self):
        # server = Server(host = test_server, port = test_port, allowed_referral_hosts = ('*', True))
        self.connection = Connection(server=None, client_strategy=STRATEGY_LDIF_PRODUCER)
        self.connection.open()

    def tearDown(self):
        self.connection.unbind()
        self.assertFalse(self.connection.bound)

    def test_add_request_to_ldif(self):
        controls = list()
        controls.append(('2.16.840.1.113719.1.27.103.7', True, 'givenName'))
        controls.append(('2.16.840.1.113719.1.27.103.7', False, 'sn'))
        if str != bytes:  # python3
            controls.append(('2.16.840.1.113719.1.27.103.7', False, bytearray('\u00e0\u00e0', encoding='UTF-8')))
        else:
            controls.append(('2.16.840.1.113719.1.27.103.7', False, bytearray(unicode('\xe0\xe0', encoding='latin1'), encoding='UTF-8')))  # for python2 compatability
        controls.append(('2.16.840.1.113719.1.27.103.7', False, 'trailingspace '))
        self.connection.add(dn_for_test(test_base, 'test-add-operation'), 'iNetOrgPerson', {'objectClass': 'iNetOrgPerson', 'sn': 'test-add', test_name_attr: 'test-add-operation'}, controls=controls)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-add-operation,o=test' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 true: givenName' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false: sn' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false:: w6DDoA==' in response)
        self.assertTrue('control: 2.16.840.1.113719.1.27.103.7 false:: dHJhaWxpbmdzcGFjZSA=' in response)
        self.assertTrue('changetype: add' in response)
        self.assertTrue('objectClass: inetorgperson' in response)
        self.assertTrue('sn: test-add' in response)
        self.assertTrue('cn: test-add-operation' in response)

    def test_delete_request_to_ldif(self):
        self.connection.strategy.order = dict(delRequest=['dn:', 'changetype', 'vers'])
        self.connection.delete(dn_for_test(test_base, 'test-del-operation'))
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-del-operation,o=test' in response)
        self.assertTrue('changetype: delete' in response)

    def test_modify_dn_request_to_ldif(self):
        result = self.connection.modify_dn(dn_for_test(test_base, 'test-modify-dn-operation'), test_name_attr + '=test-modified-dn-operation')
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-modify-dn-operation,o=test' in response)
        self.assertTrue('changetype: moddn' in response)
        self.assertTrue('newrdn: cn=test-modified-dn-operation' in response)
        self.assertTrue('deleteoldrdn: 1' in response)

    def test_move_dn_request_to_ldif(self):
        result = self.connection.modify_dn(dn_for_test(test_base, 'test-move-dn-operation'), test_name_attr + '=test-move-dn-operation', delete_old_dn=False, new_superior=test_moved)
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-move-dn-operation,o=test' in response)
        self.assertTrue('changetype: modrdn' in response)
        self.assertTrue('newrdn: cn=test-move-dn-operation' in response)
        self.assertTrue('deleteoldrdn: 0' in response)
        self.assertTrue('newsuperior: ou=moved,o=test' in response)

    def test_modify_add_to_ldif(self):
        result = self.connection.modify(dn_for_test(test_base, 'test-add-for-modify'), {'givenName': (MODIFY_ADD, ['test-modified-added'])})
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-add-for-modify,o=test' in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('add: givenName' in response)
        self.assertTrue('givenName: test-modified-added' in response)
        self.assertEqual('-', response[-1])

    def test_modify_replace_to_ldif(self):
        result = self.connection.modify(dn_for_test(test_base, 'test-add-for-modify'), {'givenName': (MODIFY_REPLACE, ['test-modified-replace'])})
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-add-for-modify,o=test' in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('replace: givenName' in response)
        self.assertTrue('givenName: test-modified-replace' in response)
        self.assertEqual('-', response[-1])

    def test_modify_delete_to_ldif(self):
        result = self.connection.modify(dn_for_test(test_base, 'test-add-for-modify'), {'givenName': (MODIFY_DELETE, ['test-modified-added2'])})
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=test-add-for-modify,o=test' in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('delete: givenName' in response)
        self.assertTrue('givenName: test-modified-added2' in response)
        self.assertEqual('-', response[-1])

    def test_multiple_modify_to_ldif(self):
        # from rfc 2849 example
        result = self.connection.modify('cn=Paula Jensen, ou=Product Development, dc=airius, dc=com',
                                        {'postaladdress': (MODIFY_ADD, ['123 Anystreet $ Sunnyvale, CA $ 94086']), 'description': (MODIFY_DELETE, []), 'telephonenumber': (MODIFY_REPLACE, ['+1 408 555 1234', '+1 408 555 5678']),
                                         'facsimiletelephonenumber': (MODIFY_DELETE, ['+1 408 555 9876'])})
        if not isinstance(result, bool):
            self.connection.get_response(result)
        response = self.connection.response
        self.assertTrue('version: 1' in response)
        self.assertTrue('dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com' in response)
        self.assertTrue('changetype: modify' in response)
        self.assertTrue('delete: facsimiletelephonenumber' in response)
        self.assertTrue('facsimiletelephonenumber: +1 408 555 9876' in response)
        self.assertTrue('replace: telephonenumber' in response)
        self.assertTrue('telephonenumber: +1 408 555 1234' in response)
        self.assertTrue('telephonenumber: +1 408 555 5678' in response)
        self.assertTrue('add: postaladdress' in response)
        self.assertTrue('postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086' in response)
        self.assertTrue('delete: description' in response)
        self.assertEqual('-', response[-1])
                                 data,
                                 auto_bind=True)
except core.exceptions.LDAPBindError as e:
    print("Error connecting to LDAP server: %s" % e)
    sys.exit(1)

# Check and see if obj exists
user_search = ldap_connection.search(base_dn,
                                     '(&(name=' + objname + ')(objectClass=' +
                                     objtype + '))',
                                     attributes=['distinguishedName'])

# Check the results
if not user_search:
    print(objtype, objname, "does not exist in AD")
    sys.exit(1)

# Add the new user account
ldap_connection.delete(obj_dn)

if ldap_connection.result['result'] != 0:
    print("Error adding new " + objtype +
          ": %s" % ldap_connection.result['description'])
    sys.exit(1)

# LDAP unbind
ldap_connection.unbind()

# All is good
print('Successfully deleted ' + obj_dn)
Exemple #40
0
class Session:
    def __init__(self):
        self.config = config
        self.ldap_config = self.config['ldap']

        url = urlparse(self.ldap_config.get('url'))

        self.server = Server(url.hostname, port=url.port, get_info=ALL)
        self.connection = Connection(self.server, auto_bind=True, client_strategy=SYNC,
                                     user=self.ldap_config.get('bind_dn'), password=self.ldap_config.get('passwd'),
                                     authentication=SIMPLE, check_names=True)

    def close(self):
        self.connection.unbind()

    def get_simple(self, search_base, attributes, search_filter=None, flaten=None):
        search_filter = search_filter or '(objectClass=*)'

        if flaten is False:
            flaten = []
        else:
            flaten = flaten or ['cn', 'uid']

        entries = self.connection.extend.standard.paged_search(search_base=search_base,
                                                               search_filter=search_filter,
                                                               search_scope=SUBTREE,
                                                               attributes=attributes,
                                                               paged_size=10,
                                                               generator=True)

        results = []

        for entry in entries:
            if len(entry['attributes']) == 0:
                continue

            result = entry['attributes']
            result['dn'] = entry['dn']

            for attribute_name in result:
                if attribute_name in flaten:
                    result[attribute_name] = result[attribute_name][0]

            results.append(result)

        return results

    def get_groups(self, attributes=None):
        attributes = attributes or ['cn', 'gidNumber', 'description']
        return self.get_simple(search_base=self.ldap_config.get('group_ou'),
                               attributes=attributes)

    def get_users(self, attributes=None):
        attributes = attributes or ['uid', 'cn', 'mail']

        casual = list(attributes)
        casual.remove('groups')

        users = self.get_simple(search_base=self.ldap_config.get('user_ou'),
                                attributes=casual)

        if 'groups' in attributes:
            for user in users:
                user['groups'] = [group['cn'] for group in self.get_user_groups(uid=user['uid'])]

        return users

    def get_user_dn(self, uid):
        return 'uid={uid},{user_ou}'.format(user_ou=self.ldap_config.get('user_ou'), uid=uid)

    def get_group_dn(self, cn):
        return 'cn={cn},{group_ou}'.format(group_ou=self.ldap_config.get('group_ou'), cn=cn)

    def get_user_groups(self, uid, attributes=None):
        user_dn = self.get_user_dn(uid)

        groups = self.get_simple(
            search_base=self.ldap_config.get('group_ou'),
            search_filter='(|(member={dn})(memberUid={uid}))'.format(dn=user_dn, uid=uid),
            attributes=attributes or ['cn'])

        return groups

    def add_user(self, username, common_name, email=None):
        attributes = {'cn': common_name,
                      'sn': common_name.split()[-1],
                      'mail': email}

        self.connection.add(
            dn="uid={username},{user_ou}".format(username=username, user_ou=self.ldap_config.get('user_ou')),
            object_class=['inetOrgPerson', 'organizationalPerson', 'person', 'top'],
            attributes=attributes)

        self.upgrade_user_schema(username=username)

    def get_top_uid(self):
        users = self.get_simple(search_base=self.ldap_config.get('user_ou'),
                                attributes=['uidNumber'])
        return max([user['uidNumber'] for user in users])

    def _migration0(self, username):
        user_dn = self.get_user_dn(uid=username)

        self.connection.modify(
            dn=user_dn,
            changes={
                'objectClass': [(MODIFY_ADD, ['posixAccount'])],
                'loginShell': [(MODIFY_ADD, ['/bin/bash'])],
                'homeDirectory': [(MODIFY_ADD, ['/home/{}'.format(username)])],
                'gidNumber': [(MODIFY_ADD, ['1999'])],
                'uidNumber': [(MODIFY_ADD, [self.get_top_uid() + 1])],
            })

    def upgrade_user_schema(self, username):
        self._migration0(username=username)

    def delete_user(self, username):
        user_dn = self.get_user_dn(uid=username)
        groups = self.get_user_groups(uid=username)

        for group in groups:
            self.delete_from_group(username=username, group=group['cn'])

        self.connection.delete(user_dn)

    def change_password(self, username, password):
        self.connection.extend.standard.modify_password(user=self.get_user_dn(uid=username),
                                                        new_password=password)

    def add_to_group(self, username, group):
        user_dn = self.get_user_dn(uid=username)
        group_dn = self.get_group_dn(cn=group)

        self.connection.modify(dn=group_dn,
                               changes={'member': [(MODIFY_ADD, [user_dn])]})

    def delete_from_group(self, username, group):
        user_dn = self.get_user_dn(uid=username)
        group_dn = self.get_group_dn(cn=group)

        self.connection.modify(dn=group_dn,
                               changes={'member': [(MODIFY_DELETE, [user_dn])]})

    def add_to_groups(self, username, groups):
        for group in groups:
            self.add_to_group(username=username, group=group)

    def delete_from_groups(self, username, groups):
        for group in groups:
            self.delete_from_group(username=username, group=group)

    def activate(self, username):
        self.add_to_group(username=username, group='members')

    def deactivate(self, username):
        self.delete_from_group(username=username, group='members')