def search_by_name(self, fullname, *args, **kwargs): """ Search for fullname in TED cn field. ``fullname`` is handled like so: * If only a single name is given, e.g. "last", the query finds objects with an exact match of the cn or objects with a matching last name (cn " last"). * If two names are given, e.g. "first last", the query finds objects with a matching first name (cn beginning with "first ") and last name. * If three or more names are given, e.g. "first middle1 middle2 last", the query finds objects with a matching first and last name that also contain the middle names (cn containing " middle1 middle2 "). Note, querying for common names will likely result in the server returning a size limit exceeded error; thus, when at all possible, you should really only be querying by EID, UIN, or ISO. """ names = fullname.split() if len(names) < 1: filter_str = '(cn=)' elif len(names) == 1: filter_str = filter_format("(|(cn=%s)(cn=* %s))", [names[0]] * 2) elif len(names) == 2: filter_str = filter_format("(&(cn=%s *)(cn=* %s))", names) else: middle_names = " ".join(names[1:-1]) filter_str = filter_format("(&(cn=%s *)(cn=* %s *)(cn=* %s))", [names[0], middle_names, names[-1]]) return self.search(filter_str, *args, **kwargs)
def __init__(self, seid, sname, stype, surl, sconfig, login, password, verbose=0): """ Create a LDAPConnection instance. """ self.config = self.configure(seid, sname, stype, surl, sconfig, login, password) self.verbose = verbose self.is_active_directory = ( self.config["user-login-attr"] == "sAMAccountName") if verbose > 0: pprint(self.config) if self.is_active_directory: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, 0) self.ldapobject = ldap.initialize(self.config["url"]) self.ldapobject.protocol_version = 3 if self.is_active_directory: self.ldapobject.set_option(ldap.OPT_REFERRALS, 0) self.ldapobject.simple_bind_s(self.config["data-cnx-dn"], self.config["data-cnx-password"]) self.user_base_filters = [ filter_format("(%s=%s)", ("objectClass", o)) for o in self.config["user-classes"]] self.group_base_filters = [ filter_format("(%s=%s)", ("objectClass", o)) for o in self.config["group-classes"]] if verbose > 0: pprint(self.user_base_filters) pprint(self.group_base_filters)
def search_by_email(self, query, nonvouched_only=False): """ Searches against the email fields for people. Returns same type of data as search. """ encoded_q = query.encode("utf-8") if nonvouched_only: q = filter_format(NONVOUCHED_EMAIL_SRCH_FLTR, (encoded_q, encoded_q)) else: q = filter_format("(|(mail=*%s*)(uid=*%s*))", (encoded_q, encoded_q)) return self._populate_people_results(self._people_search(q))
def search(self, query): """ General purpose 'quick' search. Returns a list of larper.Person objects. """ encoded_q = query.encode("utf-8") peep_esc_q = filter_format(PEEP_SRCH_FLTR, (encoded_q, encoded_q)) irc_esc_q = filter_format(IRC_SRCH_FLTR, (encoded_q,)) people = self._people_search(peep_esc_q) irc_nicks = self._irc_search(irc_esc_q) people += self._people_from_irc_results_search(irc_nicks) return self._populate_people_results(people)
def get_user_groups(self, user): """Returns a ``list`` with the user's groups or ``None`` if unsuccessful. :param str user: User we want groups for. """ conn = self.bind try: if current_app.config['LDAP_OPENLDAP']: fields = \ [str(current_app.config['LDAP_GROUP_MEMBER_FILTER_FIELD'])] records = conn.search_s( current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE, ldap_filter.filter_format( current_app.config['LDAP_GROUP_MEMBER_FILTER'], (self.get_object_details(user, dn_only=True),)), fields) else: records = conn.search_s( current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE, ldap_filter.filter_format( current_app.config['LDAP_USER_OBJECT_FILTER'], (user,)), [current_app.config['LDAP_USER_GROUPS_FIELD']]) conn.unbind_s() if records: if current_app.config['LDAP_OPENLDAP']: group_member_filter = \ current_app.config['LDAP_GROUP_MEMBER_FILTER_FIELD'] if sys.version_info[0] > 2: groups = [record[1][group_member_filter][0].decode( 'utf-8') for record in records] else: groups = [record[1][group_member_filter][0] for record in records] return groups else: if current_app.config['LDAP_USER_GROUPS_FIELD'] in \ records[0][1]: groups = records[0][1][ current_app.config['LDAP_USER_GROUPS_FIELD']] result = [re.findall(b'(?:cn=|CN=)(.*?),', group)[0] for group in groups] if sys.version_info[0] > 2: result = [r.decode('utf-8') for r in result] return result except ldap.LDAPError as e: raise LDAPException(self.error(e.args))
def search(self, query, nonvouched_only=False): """ General purpose 'quick' search. Returns a list of larper.Person objects. """ encoded_q = query.encode('utf-8') if nonvouched_only: peep_esc_q = filter_format(NONVOUCHED_SRCH_FLTR, (encoded_q, encoded_q)) else: peep_esc_q = filter_format(PEEP_SRCH_FLTR, (encoded_q, encoded_q)) irc_esc_q = filter_format(IRC_SRCH_FLTR, (encoded_q,)) people = self._people_search(peep_esc_q) irc_nicks = self._irc_search(irc_esc_q) people += self._people_from_irc_results_search(irc_nicks) return self._populate_people_results(people)
def get_member_of(self, con, search_results, seen=None, depth=0): depth += 1 if seen is None: seen = set() for name, data in search_results: if name is None: continue member_of = data.get('memberOf', []) new_groups = [x.split(',')[0].split('=')[1] for x in member_of] old_seen = seen.copy() seen.update(new_groups) # collect groups recursively if self.can_recurse(depth): for group in new_groups: if group in old_seen: continue # Search for groups with the specified CN. Use the CN # rather than The sAMAccountName so that behavior is # correct when the values differ (e.g. if a # "pre-Windows 2000" group name is set in AD) group_data = self.search_ad( con, filter_format('(&(objectClass=group)(cn=%s))', (group,))) seen.update(self.get_member_of(con, group_data, seen=seen, depth=depth)) else: logging.warning('ActiveDirectory recursive group check ' 'reached maximum recursion depth.') return seen
def authenticate(self, conf, login, password): """ Authenticate a user against the specified LDAP server. In order to prevent an unintended 'unauthenticated authentication', which is an anonymous bind with a valid dn and a blank password, check for empty passwords explicitely (:rfc:`4513#section-6.3.1`) :param dict conf: LDAP configuration :param login: username :param password: Password for the LDAP user :return: LDAP entry of authenticated user or False :rtype: dictionary of attributes """ if not password: return False entry = False filter = filter_format(conf['ldap_filter'], (login,)) try: results = self.query(conf, filter) # Get rid of (None, attrs) for searchResultReference replies results = [i for i in results if i[0]] if results and len(results) == 1: dn = results[0][0] conn = self.connect(conf) conn.simple_bind_s(dn, password) conn.unbind() entry = results[0] except ldap.INVALID_CREDENTIALS: return False except ldap.LDAPError, e: _logger.error('An LDAP exception occurred: %s', e)
def genVoicemailconfEntry(co, lo, box): from univention.admin.handlers.users import user boxUser = user.lookup(co, lo, filter_format("(ast4ucsUserMailbox=%s)", (box.dn,))) if len(boxUser) == 0: return ";; Mailbox %s has no user.\n" % box["id"] if len(boxUser) > 1: msg = ";; Mailbox %s has multiple users:\n" % box["id"] for userObj in boxUser: msg += ";; * %s\n" % userObj["username"] boxUser = boxUser[0].info box = box.info if box.get("email") == "1" and boxUser.get("mailPrimaryAddress"): return "%s => %s,%s,%s\n" % ( box["id"], box["password"], getNameFromUser(boxUser), llist(boxUser["mailPrimaryAddress"])[0], ) else: return "%s => %s,%s\n" % ( box["id"], box["password"], getNameFromUser(boxUser), )
def dict_to_filter(criteria, or_search=False, or_keys=None, or_values=None): """Turn dictionary criteria into ldap queryFilter string """ or_keys = (or_keys is None) and or_search or or_keys or_values = (or_values is None) and or_search or or_values _filter = None for attr, values in criteria.items(): attr = encode_utf8(attr) if not isinstance(values, list): values = [values] attrfilter = None for value in values: if isinstance(value, unicode): value = encode_utf8(value) valuefilter = LDAPFilter(filter_format('(%s=%s)', (attr, value))) if attrfilter is None: attrfilter = valuefilter continue if or_values: attrfilter |= valuefilter else: attrfilter &= valuefilter if _filter is None: _filter = attrfilter continue if or_keys: _filter |= attrfilter else: _filter &= attrfilter if _filter is None: _filter = LDAPFilter() return _filter
def search_by_name(self, query): """ Searches against the full_name field for people. Returns same type of data as search. """ q = filter_format("(cn=*%s*)", (query.encode('utf-8'),)) return _populate_any(self._search(q))
def search(self, query): """ General purpose 'quick' search. Returns a list of larper.Person objects. """ q = filter_format("(|(cn=*%s*)(mail=*%s*))", (query, query)) return _populate_any(self._search(q))
def check(self, db, uid, passwd): try: return super(users,self).check(db, uid, passwd) except security.ExceptionNoTb: # AccessDenied pass cr = pooler.get_db(db).cursor() user = self.browse(cr, 1, uid) logger = logging.getLogger('orm.ldap') if user and user.company_id.ldaps: for res_company_ldap in user.company_id.ldaps: try: l = ldap.open(res_company_ldap.ldap_server, res_company_ldap.ldap_server_port) if l.simple_bind_s(res_company_ldap.ldap_binddn, res_company_ldap.ldap_password): base = res_company_ldap.ldap_base scope = ldap.SCOPE_SUBTREE filter = filter_format(res_company_ldap.ldap_filter, (user.login,)) retrieve_attributes = None result_id = l.search(base, scope, filter, retrieve_attributes) timeout = 60 result_type, result_data = l.result(result_id, timeout) if result_data and result_type == ldap.RES_SEARCH_RESULT and len(result_data) == 1: dn = result_data[0][0] if l.bind_s(dn, passwd): l.unbind() self._uid_cache.setdefault(db, {})[uid] = passwd cr.close() return True l.unbind() except Exception, e: logger.warning('cannot check', exc_info=True) pass
def get_by_unique_id(self, unique_id, use_master=False): """Retrieves a person from LDAP with this unique_id. Raises NO_SUCH_PERSON if unable to find them. use_master can be set to True to force reading from master where stale data isn't acceptable. """ f = "(&(objectClass=mozilliansPerson)(uniqueIdentifier=%s))" q = filter_format(f, (unique_id,)) results = self._people_search(q, use_master) msg = 'Unable to locate %s in the LDAP directory' if not results: raise NO_SUCH_PERSON(msg % unique_id) elif len(results) == 1: _dn, attrs = results[0] # Pending users will detect the existance of another # person, but there won't be any data besides uniqueIdentifier if 'sn' not in attrs: raise NO_SUCH_PERSON(msg % unique_id) else: return Person.new_from_directory(attrs) else: msg = 'Multiple people found for %s. This should never happen.' statsd.incr('larper.errors.get_by_unique_id_has_multiple') raise INCONCEIVABLE(msg % unique_id)
def get_group_members(self, group): """Returns a ``list`` with the group's members or ``None`` if unsuccessful. :param str group: Group we want users for. """ conn = self.bind try: records = conn.search_s( current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE, ldap_filter.filter_format( current_app.config['LDAP_GROUP_OBJECT_FILTER'], (group,)), [current_app.config['LDAP_GROUP_MEMBERS_FIELD']]) conn.unbind_s() if records: if current_app.config['LDAP_GROUP_MEMBERS_FIELD'] in \ records[0][1]: members = records[0][1][ current_app.config['LDAP_GROUP_MEMBERS_FIELD']] if sys.version_info[0] > 2: members = [m.decode('utf-8') for m in members] return members except ldap.LDAPError as e: raise LDAPException(self.error(e.args))
def search_by_email(self, query): """ Searches against the email fields for people. Returns same type of data as search. """ q = filter_format("(|(mail=*%s*)(uid=*%s*))", (query, query,)) return _populate_any(self._search(q))
def reverseFieldsLoad(self): if not self.dn: return for field, foreignModule, foreignField in self.reverseFields: foreignModule = univention.admin.modules.get(foreignModule) univention.admin.modules.init(self.lo, self.position, foreignModule) objects = foreignModule.lookup(self.co, self.lo, filter_format("%s=%s", (foreignModule.mapping.mapName(foreignField), self.dn)), superordinate=self.superordinate) self.info[field] = [obj.dn for obj in objects]
def _getUserAttribute(self, username, attribute): results = self._ldap.search_s(self._base, ldap.SCOPE_SUBTREE, filter_format("(&(objectClass=person)(sAMAccountName=%s))", [username]), attrlist=[attribute]) if len(results) == 0 or results[0][0] is None: raise KeyError("%s not a valid user" % username) dn, data = results[0] if data.has_key(attribute) and len(data[attribute]) >= 1: return data[attribute][0] return None
def search_by_email(self, query): """ Searches against the email fields for people. Returns same type of data as search. """ encoded_q = query.encode("utf-8") q = filter_format("(|(mail=*%s*)(uid=*%s*))", (encoded_q, encoded_q)) return self._populate_people_results(self._people_search(q))
def format_filter(self, filters): if isinstance(filters, basestring): return filters assert len(filters) == 2, 'filters %r' % (filters,) if isinstance(filters[1], (list, tuple)): return '(%s%s)' % (filters[0], ''.join(self.format_filter(x) for x in filters[1])) else: return filter_format('(%s=%%s)' % filters[0], (filters[1],))
def genSipconfEntry(co, lo, phone): from univention.admin.handlers.users import user from univention.admin.handlers.asterisk import sipPhone, mailbox, phoneGroup import univention.admin.modules univention.admin.modules.init(lo, phone.position, user) phoneUser = user.lookup(co, lo, filter_format("(ast4ucsUserPhone=%s)", (phone.dn,))) if len(phoneUser) == 0: return ";; Phone %s has no user.\n" % phone["extension"] if len(phoneUser) > 1: msg = ";; Phone %s has multiple users:\n" % phone["extension"] for userObj in phoneUser: msg += ";; * %s\n" % userObj["username"] phoneUser = phoneUser[0].info phone = phone.info if phoneUser.get("mailbox"): phoneMailbox = mailbox.object(co, lo, None, phoneUser["mailbox"]).info callgroups = [] for group in phone.get("callgroups", []): group = phoneGroup.object(co, lo, None, group).info callgroups.append(group["id"]) pickupgroups = [] for group in phone.get("pickupgroups", []): group = phoneGroup.object(co, lo, None, group).info pickupgroups.append(group["id"]) res = "[%s](template-%s)\n" % ( phone["extension"], phone.get("profile", "default")) res += "secret=%s\n" % (phone["password"]) if phoneUser.get("extmode") == "normal": res += "callerid=\"%s\" <%s>\n" % ( getNameFromUser(phoneUser), phone["extension"]) elif phoneUser.get("extmode") == "first": firstPhone = sipPhone.object(co, lo, None, llist(phoneUser["phones"])[0]).info res += "callerid=\"%s\" <%s>\n" % ( getNameFromUser(phoneUser), firstPhone["extension"]) if phoneUser.get("mailbox"): res += "mailbox=%s\n" % (phoneMailbox["id"]) if callgroups: res += "callgroup=%s\n" % (','.join(callgroups)) if pickupgroups: res += "pickupgroup=%s\n" % (','.join(pickupgroups)) return res
def dump_users_and_groups(self): """ Dump all the users and groups. """ attrmap = collections.OrderedDict(self.config["group-attrs-map"]) ldap_attrlist = [str(elem) for elem in attrmap.keys()] cw_attrlist = attrmap.values() groupattr = collections.OrderedDict(self.config["group-attrs-map"]) group_key = groupattr.keys()[groupattr.values().index("gid")] groups_search = self.ldapobject.search_s( self.config["group-base-dn"], globals()[self.config["group-scope"]], "({0}=*)".format(group_key), ldap_attrlist) if self.verbose > 0: pprint(groups_search) groups_data = [] for _, group_info in groups_search: data = {} for key, values in group_info.items(): index = ldap_attrlist.index(key) if len(values) == 1: values = values[0] data[cw_attrlist[index]] = values groups_data.append(data) attrmap = collections.OrderedDict(self.config["user-attrs-map"]) # If the LDAP to CW mapping for user password is not specified # in the source config file, add it dynamically because 'upassword' # attribute is required on CWUser creation. if "userPassword" not in attrmap: attrmap["userPassword"] = "******" ldap_attrlist = [str(elem) for elem in attrmap.keys()] cw_attrlist = attrmap.values() searchfilter = [ filter_format("(%s=*)", (self.config["user-login-attr"], ))] searchfilter.extend(self.user_base_filters) searchstr = "(&%s)" % "".join(searchfilter) if self.verbose > 0: pprint(searchstr) users_search = self.ldapobject.search_s( self.config["user-base-dn"], globals()[self.config["user-scope"]], searchstr, ldap_attrlist) if self.verbose > 0: pprint(users_search) users_data = [] for _, user_info in users_search: data = {} for key, values in user_info.items(): index = ldap_attrlist.index(key) if len(values) == 1: values = values[0] data[cw_attrlist[index]] = values users_data.append(data) return groups_data, users_data
def search_users(query, limit, autocomplete=False): connection = ldap.initialize(settings.AUTH_LDAP_SERVER_URI) connection.set_option(ldap.OPT_PROTOCOL_VERSION, 3) if limit > 0: connection.set_option(ldap.OPT_SIZELIMIT, limit) connection.simple_bind_s(settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD) if autocomplete: filter_elems = [] if query.startswith(':'): searches = {'uid': query[1:]} else: searches = {'givenName': query, 'sn': query, 'mail': query} if ' ' in query: # e.g. 'Peter b' or 'laura toms' searches['cn'] = query for key, value in searches.items(): assert value filter_elems.append(filter_format('(%s=%s*)', (key, value))) search_filter = ''.join(filter_elems) if len(filter_elems) > 1: search_filter = '(|%s)' % search_filter else: if '@' in query and _valid_email(query): search_filter = filter_format("(mail=%s)", (query, )) elif query.startswith(':'): search_filter = filter_format("(uid=%s)", (query[1:], )) else: search_filter = filter_format("(cn=*%s*)", (query, )) attrs = ['cn', 'sn', 'mail', 'givenName', 'uid', 'objectClass'] search_filter = account_wrap_search_filter(search_filter) rs = connection.search_s("dc=mozilla", ldap.SCOPE_SUBTREE, search_filter, attrs) results = [] for each in rs: result = each[1] _expand_result(result) results.append(result) if len(results) >= limit: break return results
def search(self, query): """ General purpose 'quick' search. Returns a list of larper.Person objects. """ encoded_q = query.encode('utf-8') esc_q = filter_format("(|(cn=*%s*)(mail=*%s*))", (encoded_q, encoded_q)) return _populate_any(self._search(esc_q))
def authenticate(self, username, password): import ldap username = username.strip() user_subdomain = "" if "@" in username: username, user_subdomain = username.split("@", 1) elif "\\" in username: user_subdomain, username = username.split("\\", 1) userdomain = self.get_domain_name() if user_subdomain: userdomain = "%s.%s" % (user_subdomain, userdomain) connections = self.get_ldap_connections(userdomain) required_group = settings.AD_GROUP_NAME for con in connections: try: bind_username = "******" % (username, userdomain) logging.debug("User %s is trying to log in " "via AD" % bind_username) con.simple_bind_s(bind_username, password) user_data = self.search_ad( con, filter_format("(&(objectClass=user)(sAMAccountName=%s))", (username,)), userdomain ) if not user_data: return None if required_group: try: group_names = self.get_member_of(con, user_data) except Exception as e: logging.error( "Active Directory error: failed getting" "groups for user '%s': %s" % (username, e) ) return None if required_group not in group_names: logging.warning( "Active Directory: User %s is not in " "required group %s" % (username, required_group) ) return None return self.get_or_create_user(username, None, user_data) except ldap.SERVER_DOWN: logging.warning("Active Directory: Domain controller is down") continue except ldap.INVALID_CREDENTIALS: logging.warning("Active Directory: Failed login for user %s" % username) return None logging.error("Active Directory error: Could not contact any domain " "controller servers") return None
def _get_ldapuser(self, username, attrlist=None): from ldap import LDAPError from ldap.filter import filter_format # escape try: result = self._ldap_search(filter_format("(%s=%s)", [self._conf['ldap_username_attrib'], username]), attrlist) except LDAPError, e: print e return None
def _return_all(): """Return all LDAP records, provided no LIMITs are set.""" conn = ldap.initialize(settings.LDAP_SYNC_PROVIDER_URI) conn.bind_s(settings.LDAP_ADMIN_DN, settings.LDAP_ADMIN_PASSWORD) encoded_q = "@".encode("utf-8") search_filter = filter_format("(|(mail=*%s*)(uid=*%s*))", (encoded_q, encoded_q)) rs = conn.search_s(settings.LDAP_USERS_GROUP, ldap.SCOPE_SUBTREE, search_filter) return rs
def get_user_by_uid(uid): """Given a uniqueIdentifier, return an ldap record.""" conn = ldap.initialize(settings.LDAP_SYNC_PROVIDER_URI) conn.bind_s(settings.LDAP_ADMIN_DN, settings.LDAP_ADMIN_PASSWORD) search_filter = filter_format("(uniqueIdentifier=%s)", (uid,)) rs = conn.search_s(settings.LDAP_USERS_GROUP, ldap.SCOPE_SUBTREE, search_filter, Person.search_attrs) if rs: return rs[0]
def get_object_details(self, user=None, group=None, dn_only=False): """Returns a ``dict`` with the object's (user or group) details. :param str user: Username of the user object you want details for. :param str group: Name of the group object you want details for. :param bool dn_only: If we should only retrieve the object's distinguished name or not. Default: ``False``. """ query = None fields = None if user is not None: if not dn_only: fields = current_app.config['LDAP_USER_FIELDS'] query = ldap_filter.filter_format( current_app.config['LDAP_USER_OBJECT_FILTER'], (user,)) elif group is not None: if not dn_only: fields = current_app.config['LDAP_GROUP_FIELDS'] query = ldap_filter.filter_format( current_app.config['LDAP_GROUP_OBJECT_FILTER'], (group,)) conn = self.bind try: records = conn.search_s(current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE, query, fields) conn.unbind_s() result = {} if records: if dn_only: if current_app.config['LDAP_OPENLDAP']: if records: return records[0][0] else: if current_app.config['LDAP_OBJECTS_DN'] \ in records[0][1]: dn = records[0][1][ current_app.config['LDAP_OBJECTS_DN']] return dn[0] if type(records[0][1]) == 'dict': for k, v in list(records[0][1].items()): result[k] = v return result except ldap.LDAPError as e: raise LDAPException(self.error(e.args))
def get_user_by_email(email): """Given an email address, return an ldap record.""" conn = ldap.initialize(settings.LDAP_SYNC_PROVIDER_URI) conn.bind_s(settings.LDAP_ADMIN_DN, settings.LDAP_ADMIN_PASSWORD) encoded_q = email.encode("utf-8") search_filter = filter_format("(|(mail=*%s*)(uid=*%s*))", (encoded_q, encoded_q)) rs = conn.search_s(settings.LDAP_USERS_GROUP, ldap.SCOPE_SUBTREE, search_filter) if rs: return rs[0]
def get_member_of(self, con, search_results, seen=None, depth=0): """Return the LDAP groups for the given users. This iterates over the users specified in ``search_results`` and returns a set of groups of which those users are members. Args: con (ldap.LDAPObject): The LDAP connection used for checking groups memberships. search_results (list of tuple): The list of search results to check. This expects a result from :py:meth:`search_ad`. seen (set, optional): The set of groups that have already been seen when recursing. This is used internally by this method and should not be provided by the caller. depth (int, optional): The current recursion depth. This is used internally by this method and should not be provided by the caller. Returns: set: The group memberships found for the given users. """ depth += 1 if seen is None: seen = set() can_recurse = self.can_recurse(depth) for name, data in search_results: if name is None: continue new_groups = [] for group_dn in data.get('memberOf', []): parts = itertools.chain.from_iterable(str2dn(group_dn)) for attr, value, flags in parts: if attr.lower() == 'cn': new_groups.append(value) break old_seen = seen.copy() seen.update(new_groups) # Collect groups recursively. if not can_recurse: logger.warning( 'Recursive group check reached maximum ' 'recursion depth (%s)', depth) continue for group in new_groups: if group in old_seen: continue # Search for groups with the specified CN. Use the CN rather # than the sAMAccountName so that behavior is correct when # the values differ (e.g. if a "pre-Windows 2000" group name # is set in AD). group_data = self.search_ad( con, filter_format('(&(objectClass=group)(cn=%s))', [group])) seen.update( self.get_member_of(con, group_data, seen=seen, depth=depth)) return seen
def update_extended_attributes(lo, module, position): # X-type: (univention.admin.uldap.access, UdmModule, univention.admin.uldap.position) -> None """ Load extended attribute from |LDAP| and modify |UDM| handler. """ # add list of tabnames created by extended attributes if not hasattr(module, 'extended_attribute_tabnames'): module.extended_attribute_tabnames = [] # append UDM extended attributes properties4tabs = {} # type: Dict[str, List[EA_Layout]] overwriteTabList = [] # type: List[str] module.extended_udm_attributes = [] # type: List[univention.admin.extended_attribute] module_filter = filter_format('(univentionUDMPropertyModule=%s)', [name(module)]) if name(module) == 'settings/usertemplate': module_filter = '(|(univentionUDMPropertyModule=users/user)%s)' % (module_filter,) for dn, attrs in lo.search(base=position.getDomainConfigBase(), filter='(&(objectClass=univentionUDMProperty)%s(univentionUDMPropertyVersion=2))' % (module_filter,)): # get CLI name pname = attrs['univentionUDMPropertyCLIName'][0] object_class = attrs.get('univentionUDMPropertyObjectClass', [])[0] if name(module) == 'settings/usertemplate' and object_class == 'univentionMail' and 'settings/usertemplate' not in attrs.get('univentionUDMPropertyModule', []): continue # since "mail" is a default option, creating a usertemplate with any mail attribute would raise Object class violation: object class 'univentionMail' requires attribute 'uid' # get syntax propertySyntaxString = attrs.get('univentionUDMPropertySyntax', [''])[0] if propertySyntaxString and hasattr(univention.admin.syntax, propertySyntaxString): propertySyntax = getattr(univention.admin.syntax, propertySyntaxString) else: if lo.search(filter=filter_format(univention.admin.syntax.LDAP_Search.FILTER_PATTERN, [propertySyntaxString])): propertySyntax = univention.admin.syntax.LDAP_Search(propertySyntaxString) else: propertySyntax = univention.admin.syntax.string() # get hooks propertyHookString = attrs.get('univentionUDMPropertyHook', [''])[0] propertyHook = None if propertyHookString and hasattr(univention.admin.hook, propertyHookString): propertyHook = getattr(univention.admin.hook, propertyHookString)() register_ldap_connection = getattr(propertyHook, 'hook_ldap_connection', None) if register_ldap_connection: register_ldap_connection(lo, position) # get default value propertyDefault = attrs.get('univentionUDMPropertyDefault', [None]) # value may change try: mayChange = int(attrs.get('univentionUDMPropertyValueMayChange', ['0'])[0]) except: ud.debug(ud.ADMIN, ud.ERROR, 'modules update_extended_attributes: ERROR: processing univentionUDMPropertyValueMayChange threw exception - assuming mayChange=0') mayChange = 0 # value is editable (only via hooks or direkt module.info[] access) editable = attrs.get('univentionUDMPropertyValueNotEditable', ['0'])[0] not in ['1', 'TRUE'] copyable = attrs.get('univentionUDMPropertyCopyable', ['0'])[0] not in ['1', 'TRUE'] # value is required valueRequired = (attrs.get('univentionUDMPropertyValueRequired', ['0'])[0].upper() in ['1', 'TRUE']) # value not available for searching try: doNotSearch = int(attrs.get('univentionUDMPropertyDoNotSearch', ['0'])[0]) except: ud.debug(ud.ADMIN, ud.ERROR, 'modules update_extended_attributes: ERROR: processing univentionUDMPropertyDoNotSearch threw exception - assuming doNotSearch=0') doNotSearch = 0 # check if CA is multivalue property if attrs.get('univentionUDMPropertyMultivalue', [''])[0] == '1': multivalue = 1 map_method = None unmap_method = None else: multivalue = 0 map_method = univention.admin.mapping.ListToString unmap_method = None if propertySyntaxString == 'boolean': map_method = univention.admin.mapping.BooleanListToString unmap_method = univention.admin.mapping.BooleanUnMap # single value ==> use only first value propertyDefault = propertyDefault[0] # Show this attribute in UDM/UMC? if attrs.get('univentionUDMPropertyLayoutDisable', [''])[0] == '1': layoutDisabled = True else: layoutDisabled = False # get current language lang = locale.getlocale(locale.LC_MESSAGES)[0] ud.debug(ud.ADMIN, ud.INFO, 'modules update_extended_attributes: LANG = %s' % str(lang)) # get descriptions shortdesc = _get_translation(lang, attrs, 'univentionUDMPropertyTranslationShortDescription;entry-%s', 'univentionUDMPropertyShortDescription') longdesc = _get_translation(lang, attrs, 'univentionUDMPropertyTranslationLongDescription;entry-%s', 'univentionUDMPropertyLongDescription') # create property fullWidth = (attrs.get('univentionUDMPropertyLayoutFullWidth', ['0'])[0].upper() in ['1', 'TRUE']) module.property_descriptions[pname] = univention.admin.property( short_description=shortdesc, long_description=longdesc, syntax=propertySyntax, multivalue=multivalue, options=attrs.get('univentionUDMPropertyOptions', []), required=valueRequired, may_change=mayChange, dontsearch=doNotSearch, identifies=False, default=propertyDefault, editable=editable, copyable=copyable, size='Two' if fullWidth else None, ) # add LDAP mapping if attrs['univentionUDMPropertyLdapMapping'][0].lower() != 'objectClass'.lower(): module.mapping.register(pname, attrs['univentionUDMPropertyLdapMapping'][0], unmap_method, map_method) else: module.mapping.register(pname, attrs['univentionUDMPropertyLdapMapping'][0], univention.admin.mapping.nothing, univention.admin.mapping.nothing) if hasattr(module, 'layout'): tabname = _get_translation(lang, attrs, 'univentionUDMPropertyTranslationTabName;entry-%s', 'univentionUDMPropertyLayoutTabName', _('Custom')) overwriteTab = (attrs.get('univentionUDMPropertyLayoutOverwriteTab', ['0'])[0].upper() in ['1', 'TRUE']) # in the first generation of extended attributes of version 2 # this field was a position defining the attribute to # overwrite. now it is the name of the attribute to overwrite overwriteProp = attrs.get('univentionUDMPropertyLayoutOverwritePosition', [''])[0] if overwriteProp == '0': overwriteProp = None deleteObjectClass = (attrs.get('univentionUDMPropertyDeleteObjectClass', ['0'])[0].upper() in ['1', 'TRUE']) tabAdvanced = (attrs.get('univentionUDMPropertyLayoutTabAdvanced', ['0'])[0].upper() in ['1', 'TRUE']) groupname = _get_translation(lang, attrs, 'univentionUDMPropertyTranslationGroupName;entry-%s', 'univentionUDMPropertyLayoutGroupName') try: groupPosition = int(attrs.get('univentionUDMPropertyLayoutGroupPosition', ['-1'])[0]) except TypeError: groupPosition = 0 ud.debug(ud.ADMIN, ud.INFO, 'update_extended_attributes: extended attribute (LDAP): %s' % str(attrs)) # only one is possible ==> overwriteTab wins if overwriteTab and overwriteProp: overwriteProp = None # add tab name to list if missing if tabname not in properties4tabs and not layoutDisabled: properties4tabs[tabname] = [] ud.debug(ud.ADMIN, ud.INFO, 'modules update_extended_attributes: custom fields init for tab %s' % tabname) # remember tab for purging if required if overwriteTab and tabname not in overwriteTabList and not layoutDisabled: overwriteTabList.append(tabname) if not layoutDisabled: # get position on tab # -1 == append on top priority = attrs.get('univentionUDMPropertyLayoutPosition', ['-1'])[0] try: priority = int(priority) if priority < 1: priority = -1 except ValueError: ud.debug(ud.ADMIN, ud.WARN, 'modules update_extended_attributes: custom field for tab %s: failed to convert tabNumber to int' % tabname) priority = -1 if priority == -1 and properties4tabs[tabname]: priority = max([-1, min((ea_layout.position for ea_layout in properties4tabs[tabname])) - 1]) properties4tabs[tabname].append(EA_Layout( name=pname, tabName=tabname, position=priority, advanced=tabAdvanced, overwrite=overwriteProp, fullWidth=fullWidth, groupName=groupname, groupPosition=groupPosition, is_app_tab=any(option in [key for (key, value) in getattr(module, 'options', {}).items() if value.is_app_option] for option in attrs.get('univentionUDMPropertyOptions', [])), )) else: for tab in getattr(module, 'layout', []): tab.remove(pname) module.extended_udm_attributes.append(univention.admin.extended_attribute( name=pname, objClass=object_class, ldapMapping=attrs['univentionUDMPropertyLdapMapping'][0], deleteObjClass=deleteObjectClass, syntax=propertySyntaxString, hook=propertyHook )) # overwrite tabs that have been added by UDM extended attributes for tab in module.extended_attribute_tabnames: if tab not in overwriteTabList: overwriteTabList.append(tab) if properties4tabs: # remove layout of tabs that have been marked for replacement for tab in module.layout: if tab.label in overwriteTabList: tab.layout = [] for tabname, priofields in properties4tabs.items(): priofields = sorted(priofields) currentTab = None # get existing fields if tab has not been overwritten for tab in module.layout: if tab.label == tabname: # found tab in layout currentTab = tab # tab found ==> leave loop break else: # tab not found in current layout, so add it currentTab = Tab(tabname, tabname, advanced=True) module.layout.append(currentTab) # remember tabs that have been added by UDM extended attributes if tabname not in module.extended_attribute_tabnames: module.extended_attribute_tabnames.append(tabname) currentTab.is_app_tab = any(x.is_app_tab for x in priofields) # check if tab is empty ==> overwritePosition is impossible freshTab = len(currentTab.layout) == 0 for ea_layout in priofields: if currentTab.advanced and not ea_layout.advanced: currentTab.advanced = False # if groupName is set check if it exists, otherwise create it if ea_layout.groupName: for item in currentTab.layout: if isinstance(item, ILayoutElement) and item.label == ea_layout.groupName: break else: # group does not exist grp = Group(ea_layout.groupName) if ea_layout.groupPosition > 0: currentTab.layout.insert(ea_layout.groupPosition - 1, grp) else: currentTab.layout.append(grp) # - existing property shall be overwritten AND # - tab is not new and has not been cleaned before AND # - position >= 1 (top left position is defined as 1) AND # - old property with given position exists if currentTab.exists(ea_layout.name): continue elif ea_layout.overwrite and not freshTab: # we want to overwrite an existing property # in the global fields ... if not ea_layout.groupName: replaced, layout = currentTab.replace(ea_layout.overwrite, ea_layout.name, recursive=True) if not replaced: # the property was not found so we'll append it currentTab.layout.append(ea_layout.name) else: for item in currentTab.layout: if isinstance(item, ILayoutElement) and item.label == ea_layout.groupName: replaced, layout = item.replace(ea_layout.overwrite, ea_layout.name) if not replaced: # the property was not found so we'll append it item.layout.append(ea_layout.name) else: if not ea_layout.groupName: currentTab.insert(ea_layout.position, ea_layout.name) else: for item in currentTab.layout: if isinstance(item, ILayoutElement) and item.label == ea_layout.groupName: item.insert(ea_layout.position, ea_layout.name) break # check for properties with the syntax class LDAP_Search for pname, prop in module.property_descriptions.items(): if prop.syntax.name == 'LDAP_Search': prop.syntax._load(lo) if prop.syntax.viewonly: module.mapping.unregister(pname, False) elif univention.admin.syntax.is_syntax(prop.syntax, univention.admin.syntax.complex) and hasattr(prop.syntax, 'subsyntaxes'): for text, subsyn in prop.syntax.subsyntaxes: if subsyn.name == 'LDAP_Search': subsyn._load(lo)
def _ldap_post_remove(self): if 'posix' in self.options: univention.admin.allocators.release(self.lo, self.position, 'uidNumber', self.uidNum) groupObjects = univention.admin.handlers.groups.group.lookup(self.co, self.lo, filter_s=filter_format('uniqueMember=%s', [self.dn])) if groupObjects: for i in range(0, len(groupObjects)): groupObjects[i].open() if self.dn in groupObjects[i]['users']: groupObjects[i]['users'].remove(self.dn) groupObjects[i].modify(ignore_license=1) self.nagios_ldap_post_remove() univention.admin.handlers.simpleComputer._ldap_post_remove(self) # Need to clean up oldinfo. If remove was invoked, because the # creation of the object has failed, the next try will result in # a 'object class violation' (Bug #19343) self.oldinfo = {}
def get_member_of(self, con, search_results, seen=None, depth=0): """Return the LDAP groups for the given users. This iterates over the users specified in ``search_results`` and returns a set of groups of which those users are members. Args: con (ldap.LDAPObject): The LDAP connection used for checking groups memberships. search_results (list of tuple): The list of search results to check. This expects a result from :py:meth:`search_ad`. seen (set, optional): The set of groups that have already been seen when recursing. This is used internally by this method and should not be provided by the caller. depth (int, optional): The current recursion depth. This is used internally by this method and should not be provided by the caller. Returns: set: The group memberships found for the given users. """ depth += 1 if seen is None: seen = set() for name, data in search_results: if name is None: continue member_of = data.get('memberOf', []) new_groups = [x.split(b',')[0].split(b'=')[1] for x in member_of] old_seen = seen.copy() seen.update(new_groups) # Collect groups recursively. if self.can_recurse(depth): for group in new_groups: if group in old_seen: continue # Search for groups with the specified CN. Use the CN # rather than The sAMAccountName so that behavior is # correct when the values differ (e.g. if a # "pre-Windows 2000" group name is set in AD) group_data = self.search_ad( con, filter_format('(&(objectClass=group)(cn=%s))', (group, ))) seen.update( self.get_member_of(con, group_data, seen=seen, depth=depth)) else: logger.warning( 'Recursive group check reached maximum ' 'recursion depth (%s)', depth) return seen
def open(self): super(object, self).open() self['portalComputers'] = self.lo.searchDn(filter=filter_format('(&(objectClass=univentionPortalComputer)(univentionComputerPortal=%s))', [self.dn])) self.save()
def get_user_network_access(self, uid): users = self.ldapConnection.search(filter=filter_format( '(uid=%s)', (uid, )), attr=['univentionNetworkAccess']) return self.build_access_dict(users)
def get_groups_network_access(self, dn): groups = self.ldapConnection.search(filter=filter_format( '(uniqueMember=%s)', (dn, )), attr=['univentionNetworkAccess']) return self.build_access_dict(groups)
def acquireRange(lo, position, atype, attr, ranges, scope='base'): univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Start allocation for type = %r' % atype) startID = lo.getAttr( 'cn=%s,cn=temporary,cn=univention,%s' % (ldap.dn.escape_dn_chars(atype), position.getBase()), 'univentionLastUsedValue') univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Start ID = %r' % startID) if not startID: startID = ranges[0]['first'] univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Set Start ID to first %r' % startID) else: startID = int(startID[0]) for _range in ranges: if startID < _range['first']: startID = _range['first'] last = _range['last'] + 1 other = None while startID < last: startID += 1 univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Set Start ID %r' % startID) try: if other: # exception occurred while locking other, so atype was successfully locked and must be released univention.admin.locking.unlock(lo, position, atype, str(startID - 1), scope=scope) other = None univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Lock ID %r for %r' % (startID, atype)) univention.admin.locking.lock(lo, position, atype, str(startID), scope=scope) if atype in ('uidNumber', 'gidNumber'): # reserve the same ID for both other = 'uidNumber' if atype == 'gidNumber' else 'gidNumber' univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Lock ID %r for %r' % (startID, other)) univention.admin.locking.lock(lo, position, other, str(startID), scope=scope) except univention.admin.uexceptions.noLock: univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Cant Lock ID %r' % startID) continue except univention.admin.uexceptions.objectExists: univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Cant Lock existing ID %r' % startID) continue if atype in ('uidNumber', 'gidNumber'): _filter = filter_format('(|(uidNumber=%s)(gidNumber=%s))', (str(startID), str(startID))) else: _filter = '(%s=%d)' % (attr, startID) univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: searchfor %r' % _filter) if lo.searchDn(base=position.getBase(), filter=_filter): univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Already used ID %r' % startID) univention.admin.locking.unlock(lo, position, atype, str(startID), scope=scope) if other: univention.admin.locking.unlock(lo, position, other, str(startID), scope=scope) other = None continue univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALLOCATE: Return ID %r' % startID) if other: univention.admin.locking.unlock(lo, position, other, str(startID), scope=scope) return str(startID) raise univention.admin.uexceptions.noLock( _('The attribute %r could not get locked.') % (atype, ))
def genExtSIPPhoneEntry(co, lo, agis, extenPhone): extension = extenPhone.info["extension"] # check if this phone is managed manually (not by ast4ucs) if extenPhone.get("skipExtension") == "1": return ";; Extension %s is managed manually.\n" % ( extenPhone["extension"]) from univention.admin.handlers.users import user from univention.admin.handlers.asterisk import sipPhone, mailbox import univention.admin.modules univention.admin.modules.init(lo, extenPhone.position, user) phoneUser = user.lookup( co, lo, filter_format("(ast4ucsUserPhone=%s)", (extenPhone.dn, ))) if len(phoneUser) == 0: return ";; Phone %s has no user.\n" % extenPhone["extension"] if len(phoneUser) > 1: msg = ";; Phone %s has multiple users:\n" % extenPhone["extension"] for userObj in phoneUser: msg += ";; * %s\n" % userObj["username"] phoneUser = phoneUser[0].info try: timeout = int(phoneUser["timeout"]) if timeout < 1 or timeout > 120: raise Exception except: timeout = 10 try: ringdelay = int(phoneUser["ringdelay"]) if ringdelay < 1 or ringdelay > 120: raise Exception except: ringdelay = 0 channels = [] hints = [] for dn in phoneUser.get("phones", []): phone = sipPhone.object(co, lo, extenPhone.position, dn, extenPhone.superordinate) hints.append("SIP/%s" % phone["extension"]) if phone.get("forwarding"): channels.append("Local/%s" % phone["forwarding"]) else: channels.append("SIP/%s" % phone["extension"]) res = [] # copy agis into res for agi in agis: res.append(agi) if channels: if ringdelay: for channel in channels[:-1]: res.append("Dial(%s,%i,tT)" % (channel, ringdelay)) res.append("Wait(0.5)") res.append("Dial(%s,%i,tT)" % (channels[-1], timeout)) else: res.append("Dial(%s,%i,tT)" % ('&'.join(channels), timeout)) if phoneUser.get("mailbox"): phoneMailbox = mailbox.object(co, lo, None, phoneUser["mailbox"]).info res.append("Voicemail(%s,u)" % phoneMailbox["id"]) if phoneUser.get("forwarding"): res = ["Dial(Local/%s,,tT)" % phoneUser["forwarding"]] resStr = "" if hints: resStr += "exten => %s,hint,%s\n" % (extension, '&'.join(hints)) for i, data in enumerate(res): resStr += "exten => %s,%i,%s\n" % (extension, i + 1, data) return resStr
from samba.samdb import SamDB from samba.param import LoadParm from samba.auth import system_session from samba.credentials import Credentials from samba.ndr import ndr_unpack from samba.dcerpc import misc if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('base64_guid') args = parser.parse_args() guid = str(ndr_unpack(misc.GUID, base64.b64decode(args.base64_guid))) lp = LoadParm() creds = Credentials() creds.guess(lp) samdb = SamDB(url='/var/lib/samba/private/sam.ldb', session_info=system_session(), credentials=creds, lp=lp) domain_dn = samdb.domain_dn() res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE, expression=(filter_format("(objectGuid=%s)", (guid, ))), attrs=["dn"]) for msg in res: print(msg.get("dn", idx=0))
def ucr_overwrite_properties(module, lo): """ Overwrite properties in property_descriptions by UCR variables """ ucr_prefix = ucr_property_prefix % module.module if not module: return for var in configRegistry.keys(): if not var.startswith(ucr_prefix): continue try: prop_name, attr = var[len(ucr_prefix):].split('/', 1) # ingore internal attributes univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ucr_overwrite_properties: found variable: %s' % var) if attr.startswith('__'): continue if attr == 'default': # a property object is instantiated with default=... # but internally uses "base_default" as member variable # "default" is an instance_method... attr = 'base_default' if prop_name in module.property_descriptions: prop = module.property_descriptions[prop_name] univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ucr_overwrite_properties: found property') if hasattr(prop, attr): new_prop_val = configRegistry[var] old_prop_val = getattr(prop, attr) if old_prop_val is None: # if the attribute was None the type cast # will fail. best bet is str as type old_prop_val = '' prop_val_type = type(old_prop_val) univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ucr_overwrite_properties: set property attribute %s to %s' % (attr, new_prop_val)) if attr in ('syntax', ): if hasattr(univention.admin.syntax, new_prop_val): syntax = getattr(univention.admin.syntax, new_prop_val) setattr(prop, attr, syntax()) else: if lo.search(filter=filter_format( univention.admin.syntax.LDAP_Search. FILTER_PATTERN, [new_prop_val])): syntax = univention.admin.syntax.LDAP_Search( new_prop_val) syntax._load(lo) setattr(prop, attr, syntax) else: syntax = univention.admin.syntax.string() setattr(prop, attr, syntax()) else: setattr(prop, attr, prop_val_type(new_prop_val)) univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ucr_overwrite_properties: get property attribute: %s' % old_prop_val) univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'ucr_overwrite_properties: get property attribute (type): %s' % prop_val_type) except Exception, e: univention.debug.debug( univention.debug.ADMIN, univention.debug.ERROR, 'ucr_overwrite_properties: failed to set property attribute: %s' % str(e)) continue
def doit(arglist): ud.init('/var/log/univention/directory-manager-cmd.log', ud.FLUSH, ud.FUNCTION) out = [] opts, args = getopt.getopt(arglist[1:], '', ['binddn=', 'pwdfile=', 'user='******'pwd=']) binddn = None pwdfile = None user = None pwd = None for opt, val in opts: if opt == '--binddn': binddn = val elif opt == '--pwdfile': pwdfile = val elif opt == '--user': user = val elif opt == '--pwd': pwd = val ud.set_level(ud.LDAP, ud.ALL) ud.set_level(ud.ADMIN, ud.ALL) configRegistry = univention.config_registry.ConfigRegistry() configRegistry.load() baseDN = configRegistry['ldap/base'] with open(pwdfile) as fd: bindpw = fd.read().rstrip() ud.debug(ud.ADMIN, ud.WARN, 'binddn: %s; bindpwd: *************' % (binddn, )) try: lo = univention.admin.uldap.access( host=configRegistry['ldap/master'], port=int(configRegistry.get('ldap/master/port', '7389')), base=baseDN, binddn=binddn, bindpw=bindpw, start_tls=2) except Exception as exc: ud.debug(ud.ADMIN, ud.WARN, 'authentication error: %s' % (exc, )) out.append('authentication error: %s' % (exc, )) return out if isinstance(user, bytes): # python 2 user = user.decode('utf-8') if configRegistry.get('samba/charset/unix', 'utf8') in ['utf8', 'latin']: ud.debug( ud.ADMIN, ud.INFO, 'univention-passwd: known charset given: %s' % configRegistry.get('samba/charset/unix')) if not isinstance(pwd, bytes): # python 3 pwd = pwd.encode('UTF-8') pwd = pwd.decode(configRegistry.get('samba/charset/unix', 'utf8')) else: ud.debug(ud.ADMIN, ud.INFO, 'univention-passwd: unknown charset given, try fallback') if isinstance(pwd, bytes): # python 2 pwd = pwd.decode('utf-8') try: dn = lo.searchDn(filter=filter_format( u'(&(uid=%s)(|(objectClass=posixAccount)(objectClass=sambaSamAccount)(objectClass=person)))', [user]), base=baseDN, unique=True) position = univention.admin.uldap.position(baseDN) module = univention.admin.modules.get('users/user') univention.admin.modules.init(lo, position, module) object = univention.admin.objects.get(module, None, lo, position=position, dn=dn[0]) object.open() # hack, to prevent that attributes belonging to the samba option are changed; Bug #41530 if 'samba' in object.options: object.options.remove('samba') object.old_options.remove('samba') object._ldap_object_classes = lambda ml: ml object['password'] = pwd ud.debug(ud.ADMIN, ud.INFO, 'univention-passwd: passwd set, modify object') dn = object.modify() out.append('password changed') ud.debug(ud.ADMIN, ud.INFO, 'univention-passwd: password changed') except univention.admin.uexceptions.pwalreadyused: out.append('passwd error: password already used') return out except Exception as exc: ud.debug(ud.ADMIN, ud.WARN, 'passwd error: %s' % (exc, )) out.append('passwd error: %s' % (exc, )) return out try: # check for local ldap server connection if configRegistry.is_true('ldap/replication/preferredpassword'): if configRegistry.get('ldap/server/type') == 'slave': if os.path.exists('/etc/ldap/rootpw.conf'): lo = univention.admin.uldap.access( lo=univention.uldap.getRootDnConnection()) dn = lo.searchDn(filter=filter_format( u'(&(uid=%s)(|(objectClass=posixAccount)(objectClass=sambaSamAccount)(objectClass=person)))', [user]), base=baseDN, unique=True) position = univention.admin.uldap.position(baseDN) module = univention.admin.modules.get('users/user') univention.admin.modules.init(lo, position, module) object = univention.admin.objects.get(module, None, lo, position=position, dn=dn[0]) object.open() object['password'] = pwd ud.debug(ud.ADMIN, ud.INFO, 'univention-passwd: passwd set, modify object') object['overridePWHistory'] = '1' object['overridePWLength'] = '1' dn = object.modify() ud.debug(ud.ADMIN, ud.INFO, 'univention-passwd: password changed') except Exception as exc: ud.debug(ud.ADMIN, ud.WARN, 'passwd error: %s' % (exc, )) return out
def change(self, role, ip, netmask, oldip=None): '''Return a dict with all necessary values for ipchange read from the current status of the system.''' # ignore link local addresses (no DHCP address received) network = ipaddr.IPv4Network('%s/%s' % (ip, netmask)) if network.IsLinkLocal(): MODULE.error('Ignore link local address change.') return lo, position = univention.admin.uldap.getAdminConnection() hmodule = univention.admin.modules.get('dns/host_record') cmodule = univention.admin.modules.get('computers/%s' % (role,)) # check if already used res = univention.admin.modules.lookup(hmodule, None, lo, scope='sub', filter=filter_format('aRecord=%s', (ip,))) if res: used_by = [] for i in res: if 'name' in i: used_by.append(i['name']) raise BadRequest('The IP address is already in used by host record(s) for: %s' % ', '.join(used_by)) # do we have a forward zone for this IP address? if oldip and oldip != ip: fmodule = univention.admin.modules.get('dns/forward_zone') for forwardobject in univention.admin.modules.lookup(fmodule, None, lo, scope='sub', superordinate=None, filter=filter_format('(aRecord=%s)', (oldip,))): forwardobject.open() forwardobject['a'].remove(oldip) forwardobject['a'].append(ip) forwardobject.modify() # remove old DNS reverse entries with old IP server = cmodule.object(None, lo, position, self.user_dn) server.open() current_ips = server['ip'] for e in server['dnsEntryZoneReverse']: if e[1] in current_ips: server['dnsEntryZoneReverse'].remove(e) # change IP server['ip'] = ip MODULE.info('Change IP to %s' % (ip,)) server.modify() # do we have a new reverse zone for this IP address? rmodule = univention.admin.modules.get('dns/reverse_zone') parts = network.network.exploded.split('.') while parts[-1] == '0': parts.pop() while parts: subnet = '.'.join(parts) parts.pop() filter = filter_format('(subnet=%s)', (subnet,)) reverseobject = univention.admin.modules.lookup(rmodule, None, lo, scope='sub', superordinate=None, filter=filter) if reverseobject: server = cmodule.object(None, lo, position, self.user_dn) server.open() server['dnsEntryZoneReverse'].append([reverseobject[0].dn, ip]) server.modify() break # Change ucs-sso entry # FIXME: this should be done for UCS-in-AD domains as well! ucr.load() sso_fqdn = ucr.get('ucs/server/sso/fqdn') if ucr.is_true('ucs/server/sso/autoregistraton', True): fmodule = univention.admin.modules.get('dns/forward_zone') forwardobjects = univention.admin.modules.lookup(fmodule, None, lo, scope='sub', superordinate=None, filter=None) for forwardobject in forwardobjects: zone = forwardobject.get('zone') if not sso_fqdn.endswith(zone): continue sso_name = sso_fqdn[:-(len(zone) + 1)] for current_ip in current_ips: records = univention.admin.modules.lookup(hmodule, None, lo, scope='sub', superordinate=forwardobject, filter=filter_format('(&(relativeDomainName=%s)(aRecord=%s))', (sso_name, current_ip))) for record in records: record.open() if oldip in record['a']: record['a'].remove(oldip) record['a'].append(ip) record.modify()
def escape(self, string, args): if self._escape: return filter_format(string, args) return string % args
def _iterate_members(self, group_dn, page_size, disable_pagination): has_pagination = not (self._force_no_pagination or disable_pagination) with self._ldap.get_connection() as conn: search_flt = filter_format("(memberOf=%s,%s)", (group_dn, self._base_dn)) search_flt = self._add_user_filter(search_flt) attributes = [self._uid_attr, self._email_attr] for user_search_dn in self._user_dns: lc = ldap.controls.libldap.SimplePagedResultsControl( criticality=True, size=page_size, cookie="" ) # Conduct the initial search for users that are a member of the group. logger.debug( "Conducting LDAP search of DN: %s and filter %s", user_search_dn, search_flt ) try: if has_pagination: msgid = conn.search_ext( user_search_dn, ldap.SCOPE_SUBTREE, search_flt, serverctrls=[lc], attrlist=attributes, ) else: msgid = conn.search( user_search_dn, ldap.SCOPE_SUBTREE, search_flt, attrlist=attributes ) except ldap.LDAPError as lde: logger.exception( "Got error when trying to search %s with filter %s: %s", user_search_dn, search_flt, str(lde), ) break while True: found_results = 0 try: if has_pagination: _, rdata, _, serverctrls = conn.result3(msgid) else: _, rdata = conn.result(msgid) # Yield any users found. for userdata in rdata: found_results = found_results + 1 yield self._build_user_information(userdata[1]) logger.debug( "Found %s users in group %s; %s", found_results, user_search_dn, search_flt, ) except ldap.NO_SUCH_OBJECT as nsoe: logger.debug( "NSO when trying to lookup results of search %s with filter %s: %s", user_search_dn, search_flt, str(nsoe), ) except ldap.LDAPError as lde: logger.exception( "Error when trying to lookup results of search %s with filter %s: %s", user_search_dn, search_flt, str(lde), ) break # If no additional results, nothing more to do. if not found_results: break # If pagination is disabled, nothing more to do. if not has_pagination: logger.debug("Pagination is disabled, no further queries") break # Filter down the controls with which the server responded, looking for the paging # control type. If not found, then the server does not support pagination and we already # got all of the results. pctrls = [ control for control in serverctrls if control.controlType == ldap.controls.SimplePagedResultsControl.controlType ] if pctrls: # Server supports pagination. Update the cookie so the next search finds the next page, # then conduct the next search. cookie = lc.cookie = pctrls[0].cookie if cookie: logger.debug( "Pagination is supported for this LDAP server; trying next page" ) msgid = conn.search_ext( user_search_dn, ldap.SCOPE_SUBTREE, search_flt, serverctrls=[lc], attrlist=attributes, ) continue else: # No additional results. logger.debug( "Pagination is supported for this LDAP server but on last page" ) break else: # Pagination is not supported. logger.debug("Pagination is not supported for this LDAP server") break
def __user_dn(self, uid): search_filter = filter_format("uid=%s", (uid,)) return next(user[0] for user in self.conn.search_s(self.base_dn, ldap.SCOPE_SUBTREE, search_filter, []))
def get_station_network_access(self, mac_address): stations = self.ldapConnection.search(filter=filter_format( '(macAddress=%s)', (mac_address, )), attr=['univentionNetworkAccess']) return self.build_access_dict(stations)
def authenticate(self, request, username, password, **kwargs): """Authenticate a user against Active Directory. This will attempt to authenticate the user against Active Directory. If the username and password are valid, a user will be returned, and added to the database if it doesn't already exist. Version Changed: 4.0: The ``request`` argument is now mandatory as the first positional argument, as per requirements in Django. Args: request (django.http.HttpRequest): The HTTP request from the caller. This may be ``None``. username (unicode): The username to authenticate. password (unicode): The user's password. **kwargs (dict, unused): Additional keyword arguments passed by the caller. Returns: django.contrib.auth.models.User: The authenticated user, or ``None`` if the user could not be authenticated for any reason. """ username = username.strip() if ldap is None: logger.error( 'Attempted to authenticate user "%s" in LDAP, but ' 'the python-ldap package is not installed!', username) return None user_subdomain = '' if '@' in username: username, user_subdomain = username.split('@', 1) elif '\\' in username: user_subdomain, username = username.split('\\', 1) userdomain = self.get_domain_name() if user_subdomain: userdomain = '%s.%s' % (user_subdomain, userdomain) required_group = settings.AD_GROUP_NAME if isinstance(required_group, six.text_type): required_group = required_group.encode('utf-8') if isinstance(username, six.text_type): username_bytes = username.encode('utf-8') else: username_bytes = username if isinstance(user_subdomain, six.text_type): user_subdomain = user_subdomain.encode('utf-8') if isinstance(password, six.text_type): password = password.encode('utf-8') for uri, connection in self.get_ldap_connections(userdomain): try: bind_username = b'%s@%s' % (username_bytes, userdomain) connection.simple_bind_s(bind_username, password) user_data = self.search_ad( connection, filter_format('(&(objectClass=user)(sAMAccountName=%s))', (username_bytes, )), userdomain) if not user_data: return None if required_group: try: group_names = self.get_member_of(connection, user_data) except Exception as e: logger.error( 'Unable to retrieve groups for user ' '"%s" from controller "%s": %s', username, uri, e, exc_info=1) return None if required_group not in group_names: logger.warning( 'User %s is not in required group "%s" ' 'on controller "%s"', username, required_group, uri) return None return self.get_or_create_user(username=username, request=request, ad_user_data=user_data) except ldap.SERVER_DOWN: logger.warning('domain controller "%s" is down', uri) continue except ldap.INVALID_CREDENTIALS: logger.warning('Failed login for user "%s" on controller "%s"', username, uri) return None logger.error('Could not contact any domain controller servers') return None
def _ldap_post_move(self, olddn): for obj in univention.admin.modules.lookup('settings/portal_entry', None, self.lo, scope='sub', filter=filter_format('portal=%s', [olddn])): obj.open() obj['portal'] = [x for x in obj.info.get('portal', []) + [self.dn] if not self.lo.compare_dn(x, olddn)] obj.modify()
def is_blacklisted(self, username, ldap_connection=None, ldap_position=None): def listize(li): return [x.lower() for x in map(str.strip, li.split(",")) if x] bl_users = listize( ucr.get("umc/self-service/passwordreset/blacklist/users", "")) bl_groups = listize( ucr.get("umc/self-service/passwordreset/blacklist/groups", "")) wh_users = listize( ucr.get("umc/self-service/passwordreset/whitelist/users", "")) wh_groups = listize( ucr.get("umc/self-service/passwordreset/whitelist/groups", "")) username = self.email2username(username) # user blacklist if username.lower() in bl_users: MODULE.info( "is_blacklisted({}): match in blacklisted users".format( username)) return True # get groups try: filter_s = filter_format("(|(uid=%s)(mailPrimaryAddress=%s))", (username, username)) userdn = ldap_connection.search(filter=filter_s)[0][0] groups_dns = self.get_groups(userdn) for group_dn in list(groups_dns): groups_dns.extend(self.get_nested_groups(group_dn)) groups_dns = list(set(groups_dns)) gr_names = map(str.lower, self.dns_to_groupname(groups_dns)) except IndexError: # no user or no group found return True # group blacklist if any(gr in bl_groups for gr in gr_names): MODULE.info( "is_blacklisted({}): match in blacklisted groups".format( username)) return True # if not on blacklist, check whitelists # user whitelist if username.lower() in wh_users: MODULE.info( "is_blacklisted({}): match in whitelisted users".format( username)) return False # group whitelist if any(gr in wh_groups for gr in gr_names): MODULE.info( "is_blacklisted({}): match in whitelisted groups".format( username)) return False # not on either black or white list -> not allowed if whitelist exists, else OK MODULE.info( "is_blacklisted({}): neither black nor white listed".format( username)) return bool(wh_users or wh_groups)
def open(self): univention.admin.handlers.simpleLdap.open(self) self.updateLastUsedValue = True try: caching_timeout = int( configRegistry.get( 'directory/manager/web/modules/groups/group/caching/uniqueMember/timeout', '300')) self.cache_uniqueMember.set_timeout(caching_timeout) except: pass if 'samba' in self.options: sid = self.oldattr.get('sambaSID', [''])[0] pos = sid.rfind('-') self.info['sambaRID'] = sid[pos + 1:] if self.exists(): self['memberOf'] = self.lo.searchDn(filter=filter_format( '(&(objectClass=posixGroup)(uniqueMember=%s))', [self.dn])) time_start = time.time() self['users'] = [] self['hosts'] = [] self['nestedGroup'] = [] for i in self.oldattr.get('uniqueMember', []): if cache_uniqueMember.is_valid(i): membertype = cache_uniqueMember.get(i).get('type') if membertype == 'user': self['users'].append(i) elif membertype == 'group': self['nestedGroup'].append(i) elif membertype == 'host': self['hosts'].append(i) elif i.startswith('uid='): self['users'].append(i) cache_uniqueMember.set(i, {'type': 'user'}) else: result = self.lo.getAttr(i, 'objectClass') if result: if 'univentionGroup' in result: self['nestedGroup'].append(i) cache_uniqueMember.set(i, {'type': 'group'}) elif 'univentionHost' in result: self['hosts'].append(i) cache_uniqueMember.set(i, {'type': 'host'}) else: self['users'].append(i) else: # removing following line breaks deletion of computers from groups self['users'].append(i) time_end = time.time() ud.debug( ud.ADMIN, ud.INFO, 'groups/group: open(): member check duration: %1.2fs' % (time_end - time_start)) self['allowedEmailUsers'] = self.oldattr.get( 'univentionAllowedEmailUsers', []) self['allowedEmailGroups'] = self.oldattr.get( 'univentionAllowedEmailGroups', []) self.save()
def _ldap_pre_modify(self): # check for membership in a quota-printerclass # cut off '/' at the beginning of the destination if it exists and protocol is file:/ if self['uri'] and self['uri'][0] == 'file:/' and self['uri'][1][0] == '/': self['uri'][1] = re.sub(r'^/+', '', self['uri'][1]) if self.hasChanged('setQuota') and self.info['setQuota'] == '0' and self.info.get('spoolHost'): printergroups_filter = '(&(objectClass=univentionPrinterGroup)(univentionPrinterQuotaSupport=1)(|%s))' % (''.join(filter_format('(univentionPrinterSpoolHost=%s)', [x]) for x in self.info['spoolHost'])) group_cn = [] for pg_dn, member_list in self.lo.search(filter=printergroups_filter, attr=['univentionPrinterGroupMember', 'cn']): for member_cn in member_list.get('univentionPrinterGroupMember', []): if member_cn == self.info['name']: group_cn.append(member_list['cn'][0]) if len(group_cn) > 0: raise univention.admin.uexceptions.leavePrinterGroup(_('%(name)s is member of the following quota printer groups %(groups)s') % {'name': self.info['name'], 'groups': ', '.join(group_cn)})