class LdapPersistence(BasePersistence): def __init__(self, manager): self.client = LdapClient(manager) def get_auth_config(self): entry = self.client.get( "ou=jans-auth,ou=configuration,o=jans", attributes=["jansRevision", "jansConfWebKeys", "jansConfDyn"], ) if not entry: return {} config = { "id": entry.entry_dn, "jansRevision": entry["jansRevision"][0], "jansConfWebKeys": entry["jansConfWebKeys"][0], "jansConfDyn": entry["jansConfDyn"][0], } return config def modify_auth_config(self, id_, rev, conf_dynamic, conf_webkeys): modified, _ = self.client.modify( id_, { 'jansRevision': [(self.client.MODIFY_REPLACE, [str(rev)])], 'jansConfWebKeys': [(self.client.MODIFY_REPLACE, [json.dumps(conf_webkeys)])], 'jansConfDyn': [ (self.client.MODIFY_REPLACE, [json.dumps(conf_dynamic)]) ], }) return modified
def wait_for_ldap_conn(manager, **kwargs): """Wait for readiness/availability of LDAP server based on connection status. :param manager: An instance of :class:`~jans.pycloudlib.manager._Manager`. """ connected = LdapClient(manager).is_connected() if not connected: raise WaitError("LDAP is unreachable")
class LdapPersistence: def __init__(self, manager): self.client = LdapClient(manager) def get_auth_config(self): # base DN for auth config dn = "ou=jans-auth,ou=configuration,o=jans" entry = self.client.get(dn) if not entry: return {} return entry["jansConfDyn"][0]
def wait_for_ldap(manager, **kwargs): """Wait for readiness/availability of LDAP server based on existing entry. :param manager: An instance of :class:`~jans.pycloudlib.manager._Manager`. """ persistence_type = os.environ.get("CN_PERSISTENCE_TYPE", "ldap") ldap_mapping = os.environ.get("CN_PERSISTENCE_LDAP_MAPPING", "default") # a minimum service stack is having config-api client jca_client_id = manager.config.get("jca_client_id") default_search = ( f"inum={jca_client_id},ou=clients,o=jans", "(objectClass=jansClnt)", ) if persistence_type == "hybrid": # `cache` and `token` mapping only have base entries search_mapping = { "default": default_search, "user": ("inum=60B7,ou=groups,o=jans", "(objectClass=jansGrp)"), "site": ("ou=cache-refresh,o=site", "(ou=cache-refresh)"), "cache": ("ou=cache,o=jans", "(ou=cache)"), "token": ("ou=tokens,o=jans", "(ou=tokens)"), "session": ("ou=sessions,o=jans", "(ou=sessions)"), } search = search_mapping[ldap_mapping] else: search = default_search client = LdapClient(manager) entries = client.search(search[0], search[1], attributes=["objectClass"], limit=1) if not entries: raise WaitError("LDAP is not fully initialized")
def __init__(self, manager): self.client = LdapClient(manager) self.manager = manager
class LDAPBackend: def __init__(self, manager): self.client = LdapClient(manager) self.manager = manager def check_indexes(self, mapping): if mapping == "site": index_name = "jansScrTyp" backend = "site" # elif mapping == "statistic": # index_name = "jansMetricTyp" # backend = "metric" else: index_name = "del" backend = "userRoot" dn = "ds-cfg-attribute={},cn=Index,ds-cfg-backend-id={}," \ "cn=Backends,cn=config".format(index_name, backend) max_wait_time = 300 sleep_duration = 10 for _ in range(0, max_wait_time, sleep_duration): try: if self.client.get(dn, attributes=["1.1"]): return reason = f"Index {dn} is not ready" except (LDAPSessionTerminatedByServerError, LDAPSocketOpenError) as exc: reason = exc logger.warning("Waiting for index to be ready; reason={}; " "retrying in {} seconds".format(reason, sleep_duration)) time.sleep(sleep_duration) def import_ldif(self): optional_scopes = json.loads(self.manager.config.get("optional_scopes", "[]")) ldif_mappings = get_ldif_mappings(optional_scopes) # hybrid means only a subsets of ldif are needed persistence_type = os.environ.get("CN_PERSISTENCE_TYPE", "ldap") ldap_mapping = os.environ.get("CN_PERSISTENCE_LDAP_MAPPING", "default") if persistence_type == "hybrid": mapping = ldap_mapping ldif_mappings = {mapping: ldif_mappings[mapping]} # # these mappings require `base.ldif` # opt_mappings = ("user", "token",) # `user` mapping requires `o=gluu` which available in `base.ldif` # if mapping in opt_mappings and "base.ldif" not in ldif_mappings[mapping]: if "base.ldif" not in ldif_mappings[mapping]: ldif_mappings[mapping].insert(0, "base.ldif") ctx = prepare_template_ctx(self.manager) for mapping, files in ldif_mappings.items(): self.check_indexes(mapping) for file_ in files: logger.info(f"Importing {file_} file") src = f"/app/templates/{file_}" dst = f"/app/tmp/{file_}" os.makedirs(os.path.dirname(dst), exist_ok=True) render_ldif(src, dst, ctx) with open(dst, "rb") as fd: parser = LDIFParser(fd) for dn, entry in parser.parse(): self.add_entry(dn, entry) def add_entry(self, dn, attrs): max_wait_time = 300 sleep_duration = 10 for _ in range(0, max_wait_time, sleep_duration): try: added, msg = self.client.add(dn, attributes=attrs) if not added and "name already exists" not in msg: logger.warning(f"Unable to add entry with DN {dn}; reason={msg}") break except (LDAPSessionTerminatedByServerError, LDAPSocketOpenError) as exc: logger.warning(f"Unable to add entry with DN {dn}; reason={exc}; retrying in {sleep_duration} seconds") time.sleep(sleep_duration) def initialize(self): def is_initialized(): persistence_type = os.environ.get("CN_PERSISTENCE_TYPE", "ldap") ldap_mapping = os.environ.get("CN_PERSISTENCE_LDAP_MAPPING", "default") # a minimum service stack is having oxTrust, hence check whether entry # for oxTrust exists in LDAP default_search = ("ou=jans-auth,ou=configuration,o=jans", "(objectClass=jansAppConf)") if persistence_type == "hybrid": # `cache` and `token` mapping only have base entries search_mapping = { "default": default_search, "user": ("inum=60B7,ou=groups,o=jans", "(objectClass=jansGrp)"), "site": ("ou=cache-refresh,o=site", "(ou=people)"), "cache": ("o=jans", "(ou=cache)"), "token": ("ou=tokens,o=jans", "(ou=tokens)"), "session": ("ou=sessions,o=jans", "(ou=sessions)"), } search = search_mapping[ldap_mapping] else: search = default_search return self.client.search(search[0], search[1], attributes=["objectClass"], limit=1) should_skip = as_boolean( os.environ.get("CN_PERSISTENCE_SKIP_INITIALIZED", False), ) if should_skip and is_initialized(): logger.info("LDAP backend already initialized") return self.import_ldif()
def __init__(self, manager): super().__init__() self.manager = manager self.client = LdapClient(manager) self.type = "ldap"
class LDAPBackend(BaseBackend): def __init__(self, manager): super().__init__() self.manager = manager self.client = LdapClient(manager) self.type = "ldap" def get_entry(self, key, filter_="", attrs=None, **kwargs): def format_attrs(attrs): _attrs = {} for k, v in attrs.items(): if len(v) < 2: v = v[0] _attrs[k] = v return _attrs filter_ = filter_ or "(objectClass=*)" entry = self.client.get(key, filter_=filter_, attributes=attrs) if not entry: return None return Entry(entry.entry_dn, format_attrs(entry.entry_attributes_as_dict)) def modify_entry(self, key, attrs=None, **kwargs): attrs = attrs or {} del_flag = kwargs.get("delete_attr", False) if del_flag: mod = self.client.MODIFY_DELETE else: mod = self.client.MODIFY_REPLACE for k, v in attrs.items(): if not isinstance(v, list): v = [v] attrs[k] = [(mod, v)] return self.client.modify(key, attrs) def update_people_entries(self): # add jansAdminUIRole to default admin user admin_inum = self.manager.config.get("admin_inum") id_ = f"inum={admin_inum},ou=people,o=jans" kwargs = {} entry = self.get_entry(id_, **kwargs) if not entry: return if "jansAdminUIRole" not in entry.attrs: entry.attrs["jansAdminUIRole"] = ["api-admin"] self.modify_entry(id_, entry.attrs, **kwargs) def update_scopes_entries(self): # add jansAdminUIRole claim to profile scope kwargs = {} entry = self.get_entry(self.jans_admin_ui_role_id, **kwargs) if not entry: return if self.jans_admin_ui_claim not in entry.attrs["jansClaim"]: entry.attrs["jansClaim"].append(self.jans_admin_ui_claim) self.modify_entry(self.jans_admin_ui_role_id, entry.attrs, **kwargs) def update_clients_entries(self): jca_client_id = self.manager.config.get("jca_client_id") id_ = f"inum={jca_client_id},ou=clients,o=jans" kwargs = {} entry = self.get_entry(id_, **kwargs) if not entry: return should_update = False # modify redirect UI of config-api client hostname = self.manager.config.get("hostname") if f"https://{hostname}/admin" not in entry.attrs["jansRedirectURI"]: entry.attrs["jansRedirectURI"].append(f"https://{hostname}/admin") should_update = True # add jans_stat, SCIM users.read, SCIM users.write scopes to config-api client for scope in (self.jans_scim_scopes + self.jans_stat_scopes): if scope not in entry.attrs["jansScope"]: entry.attrs["jansScope"].append(scope) should_update = True if should_update: self.modify_entry(id_, entry.attrs, **kwargs) def update_scim_scopes_entries(self): # add jansAttrs to SCIM users.read and users.write scopes ids = self.jans_scim_scopes kwargs = {} for id_ in ids: entry = self.get_entry(id_, **kwargs) if not entry: continue if "jansAttrs" not in entry.attrs: entry.attrs["jansAttrs"] = self.jans_attrs self.modify_entry(id_, entry.attrs, **kwargs) def update_base_entries(self): # add jansManagerGrp to base entry entry = self.get_entry(JANS_BASE_ID) if not entry: return if not entry.attrs.get("jansManagerGrp"): entry.attrs["jansManagerGrp"] = JANS_MANAGER_GROUP self.modify_entry(JANS_BASE_ID, entry.attrs)