async def test_create_user(fake_user, udm_kwargs): user_data = fake_user() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.new() obj.policies = user_data.policies obj.superordinate = user_data.superordinate obj.position = user_data.position obj.options = user_data.options for k, v in attr.asdict(user_data.props).items(): setattr(obj.props, k, v) assert obj.dn is None assert obj.uri == "" res = await obj.save() assert res is obj assert obj.dn not in (None, "") assert obj.uri not in (None, "") obj_new = await mod.get(obj.dn) assert obj_new.dn == obj.dn assert obj_new.position == obj.position assert obj_new.props.firstname == obj.props.firstname assert obj_new.props.lastname == obj.props.lastname assert obj_new.props.birthday == obj.props.birthday await obj.delete()
async def test_session_base_dn(base_dn, udm_kwargs): base_dn_via_ldap = base_dn async with UDM(**udm_kwargs) as udm: base_dn_via_http = await udm.session.base_dn assert base_dn_via_http == base_dn_via_ldap
async def test_saving_stale_obj_fails(user_created_via_http, udm_kwargs, random_name): # scenario: a different channel was used to delete or rename the obj on # the server directly after saving it, so that a reload() fails, then it # should not be possible to save() again dn, url, user = user_created_via_http() async def _func(): pass async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.uri == url assert obj.props.firstname == user["properties"]["firstname"] # OK, got a valid object from LDAP, now modify it but skip the reload obj.props.firstname = random_name() obj.reload = _func await obj.save() obj.props.firstname = random_name() with warnings.catch_warnings(record=True) as caught_warnings: await obj.save() assert base_http.StaleObjectWarning in [ w.category for w in caught_warnings ]
def test_session_warn_insecure_request(): for i in range(4, 10): with warnings.catch_warnings(record=True) as w: UDM("A", "B", "http://foo.bar/baz") assert len(w) == 1 assert issubclass(w[-1].category, base_http.InsecureRequestWarning) assert "unencrypted" in str(w[-1].message)
async def test_openapi_class(udm_kwargs): async with UDM(**udm_kwargs) as udm: for name in ("groups/group", "shares/share", "users/user"): mod = udm.get(name) kls = mod.session.openapi_class(name) assert inspect.isclass(kls) assert kls.__name__.endswith("Api")
async def test_move_user(new_cn, user_created_via_http, udm_kwargs): old_user_dn, old_user_url, old_user_data = user_created_via_http() cn_dn, cn_obj_url, cn_data = new_cn() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(old_user_dn) assert obj.dn == old_user_dn assert obj.uri == old_user_url assert obj.position == old_user_data["position"] assert obj.props.firstname == old_user_data["properties"]["firstname"] obj.position = cn_dn res = await obj.save() assert res is obj assert obj.dn != old_user_dn assert obj.dn == f"uid={obj.props.username},{cn_dn}" assert obj.uri != old_user_url from urllib.parse import unquote assert unquote( obj.uri) == old_user_url.rsplit("/", 1)[0] + "/" + obj.dn obj_new = await mod.get(obj.dn) assert obj_new.dn == obj.dn assert obj_new.uri == obj.uri assert obj_new.position == cn_dn assert obj.props.firstname == old_user_data["properties"]["firstname"]
async def test_move_multiple_objects(base_dn, new_cn, user_created_via_http, udm_kwargs): top_cn_dn, top_cn_obj_url, top_cn_data = new_cn() old_cn_dn, old_cn_obj_url, old_cn_data = new_cn(position=top_cn_dn) cn_name = old_cn_data["properties"]["name"] users = dict( (num, user_created_via_http(position=old_cn_dn)) for num in range(20)) with patch.object(base_http, "MIN_FOLLOW_REDIRECT_SLEEP_TIME", 3.0): async with UDM(**udm_kwargs) as udm: mod_user = udm.get("users/user") for dn, url, data in users.values(): user_obj = await mod_user.get(dn) assert user_obj.position == old_cn_dn mod_cn = udm.get("container/cn") cn_obj = await mod_cn.get(old_cn_dn) assert cn_obj.dn == old_cn_dn assert cn_obj.dn != base_dn cn_obj.position = base_dn await cn_obj.save() assert cn_obj.dn == f"cn={cn_name},{base_dn}" for dn, url, data in users.values(): query = dn.split(",", 1)[0] async for obj in mod_user.search(query): assert old_cn_dn not in obj.dn assert obj.position == cn_obj.dn assert obj.dn == f"uid={data['properties']['username']},{cn_obj.dn}"
async def test_reload_new_obj(udm_kwargs): async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.new() assert obj.dn is None assert obj.uri == "" with pytest.raises(NotYetSavedError): await obj.reload()
async def test_openapi_method(udm_kwargs): async with UDM(**udm_kwargs) as udm: for name in ("groups/group", "shares/share", "users/user"): mod = udm.get(name) kls = mod.session.openapi_class(name) name_snake_case = "_".join(s.lower() for s in name.split("/")) for meth in base_http.METHOD_NAMES.values(): assert hasattr(kls, meth.format(name_snake_case))
async def test_bad_credentials(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() bad_kwargs = udm_kwargs.copy() bad_kwargs["password"] = f"A{udm_kwargs['password']}B" async with UDM(**bad_kwargs) as udm: mod = udm.get("users/user") with pytest.raises(APICommunicationError) as exc_info: await mod.get(dn) assert exc_info.value.status == 401
async def test_get_no_object(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() dn_parts = dn.split(",") wrong_dn = f"{dn_parts[0]}a,{','.join(dn_parts[1:])}" async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") with pytest.raises(NoObject): await mod.get(wrong_dn)
async def test_udm_obj_by_dn_good_dn(udm_kwargs, user_created_via_http): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: obj = await udm.obj_by_dn(dn) assert obj.dn == dn assert obj.uri == url assert obj.position == user["position"] assert obj.props.firstname == user["properties"]["firstname"] assert obj.props.lastname == user["properties"]["lastname"]
async def test_get_users_self_redirects_to_users_user( udm_kwargs, test_server_configuration): administrator_dn = test_server_configuration.user_dn async with UDM(**udm_kwargs) as udm: mod = udm.get("users/self") obj = await mod.get(administrator_dn) assert obj.dn == administrator_dn assert obj.props.username == "Administrator" assert obj._udm_module.name == "users/user"
def test_session_warn_min_client_tasks(): for i in range(-10, 10): with warnings.catch_warnings(record=True) as w: udm = UDM("A", "B", "https://foo.bar/baz", max_client_tasks=i) assert udm.session.max_client_tasks >= 4 assert (udm.session.openapi_client_config.connection_pool_maxsize >= udm.session.max_client_tasks) if i < 4: assert len(w) == 1 assert issubclass(w[-1].category, base_http.BadSettingsWarning) assert "max_client_tasks" in str(w[-1].message)
async def test_search_existing_user(user_created_via_http, udm_kwargs): dn, url, data = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") query = dn.split(",", 1)[0] async for obj in mod.search(query): assert obj.dn == dn assert obj.uri == url assert obj.position == data["position"] assert obj.props.firstname == data["properties"]["firstname"] assert obj.props.lastname == data["properties"]["lastname"]
async def test_delete_non_existent_user_is_ignored(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj1 = await mod.get(dn) obj2 = await mod.get(dn) await obj1.delete() with pytest.raises(NoObject): await mod.get(dn) await obj2.delete()
async def test_move_error(base_dn, random_name, user_created_via_http, udm_kwargs): old_user_dn, old_user_url, old_user_data = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(old_user_dn) assert obj.dn == old_user_dn assert obj.uri == old_user_url obj.position = f"cn={random_name},{base_dn}" with pytest.raises(MoveError): await obj.save()
async def test_new_user(base_dn, udm_kwargs): async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.new() assert obj.dn is None assert obj.uri == "" assert obj.position == f"cn=users,{base_dn}" assert obj.props.firstname is None assert obj.props.groups == [] assert obj.props.primaryGroup == f"cn=Domain Users,cn=groups,{base_dn}" assert obj.props.shell == "/bin/bash"
async def test_get_user(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.uri == url assert obj.position == user["position"] assert obj.props.firstname == user["properties"]["firstname"] assert obj.props.lastname == user["properties"]["lastname"] assert obj.props.birthday == user["properties"]["birthday"]
async def test_search_at_dn(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") with pytest.raises(ValueError): [_ async for _ in mod.search(base=dn, scope=fake.pystr())] async for obj in mod.search(base=dn, scope="base"): assert obj.dn == dn assert obj.uri == url assert obj.position == user["position"] assert obj.props.firstname == user["properties"]["firstname"] assert obj.props.lastname == user["properties"]["lastname"]
async def test_saving_obj_with_bad_property_value(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.uri == url assert obj.props.firstname == user["properties"]["firstname"] # OK, got a valid object from LDAP, now modify it but skip the reload obj.props.birthday = fake.pystr() with pytest.raises(ModifyError): await obj.save()
async def test_creating_obj_with_bad_property_value(fake_user, udm_kwargs): user_data = fake_user() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.new() obj.policies = user_data.policies obj.superordinate = user_data.superordinate obj.position = user_data.position obj.options = user_data.options for k, v in attr.asdict(user_data.props).items(): setattr(obj.props, k, v) obj.props.birthday = fake.pystr() with pytest.raises(CreateError): await obj.save()
async def test_dn_property_encoder_user_group_obj(user_created_via_http, udm_kwargs, base_dn): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.uri == url primary_group = obj.props.primaryGroup assert str(primary_group).startswith("cn=") assert str(primary_group).endswith(base_dn) assert hasattr(primary_group, "obj") primary_group_obj = await primary_group.obj assert isinstance(primary_group_obj, base_http.UdmObject) assert primary_group_obj.dn == str(primary_group) assert dn in primary_group_obj.props.users
async def test_reload_user(user_created_via_http, modify_user_via_http, udm_kwargs, user_class, random_name): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.uri == url assert obj.position == user["position"] assert obj.props.firstname == user["properties"]["firstname"] assert obj.props.lastname == user["properties"]["lastname"] assert obj.props.birthday == user["properties"]["birthday"] user_mod_data = user_class( dn="", options=None, policies=None, position=None, props={ "firstname": random_name(), "lastname": random_name() }, superordinate=None, uri="", uuid="", ) modify_user_via_http(dn, user_mod_data) res = await obj.reload() assert res is obj assert obj.dn == dn assert obj.uri == url assert obj.position == user["position"] assert obj.props.firstname == user_mod_data.props["firstname"] assert obj.props.lastname == user_mod_data.props["lastname"] assert obj.props.birthday == user["properties"]["birthday"] obj_new = await mod.get(dn) assert obj_new.dn == obj.dn assert obj_new.uri == obj.uri assert obj_new.position == obj.position assert obj_new.props.firstname == obj.props.firstname assert obj_new.props.lastname == obj.props.lastname assert obj_new.props.birthday == obj.props.birthday
async def test_object_repr(udm_kwargs): async with UDM(**udm_kwargs) as udm: mod_name = "users/user" mod = udm.get(mod_name) mod_repr = repr(mod) assert mod_repr == f"UdmModule({mod_name!r})" meta_repr = repr(mod.meta) assert meta_repr.startswith("UdmModuleMetadata(") assert "supported_api_versions" in meta_repr async for obj in mod.search(): obj_repr = repr(obj) assert obj_repr == f"UdmObject({mod_name!r}, {obj.dn!r})" props_repr = repr(obj.props) assert props_repr.startswith("UdmObjectProperties(") assert "username" in props_repr assert "firstname" in props_repr assert len(props_repr.split("\n")) > 10 break
async def test_search_all_users(base_dn, ldap_connection, udm_kwargs): with ldap_connection(connection_kwargs={"read_only": True}) as conn: logger.info("Successful LDAP login.") conn.search( search_base=base_dn, search_filter="(univentionObjectType=users/user)", attributes=[NO_ATTRIBUTES], ) dns_via_ldap = {result.entry_dn for result in conn.entries} dns_via_udm_http = set() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") async for obj in mod.search(): dns_via_udm_http.add(obj.dn) assert any(dn.startswith("uid=Administrator") for dn in dns_via_udm_http) assert dns_via_ldap == dns_via_udm_http
async def test_to_dict_user(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.props.lastname == user["properties"]["lastname"] assert obj.props.birthday == user["properties"]["birthday"] obj.props.birthday = datetime.date(1987, 6, 1) dict_repr = obj.to_dict() for k in ("dn", "options", "policies", "position", "superordinate", "uuid"): assert dict_repr[k] == getattr(obj, k) for k, v in dict_repr["props"].items(): if k == "birthday": assert v == obj.props.birthday.strftime("%Y-%m-%d") continue assert v == getattr(obj.props, k)
async def test_delete_user(user_created_via_http, udm_kwargs): dn, url, user = user_created_via_http() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(dn) assert obj.dn == dn assert obj.props.firstname == user["properties"]["firstname"] res = await obj.delete() assert res is None assert obj.dn == dn with pytest.raises(DeletedError): await obj.reload() with pytest.raises(DeletedError): await obj.save() # nothing should happen await obj.delete()
async def test_obj_by_dn(base_dn, ldap_connection, udm_kwargs): async def load_obj_by_dn(udm, result): dn = result.entry_dn object_type = result["univentionObjectType"].value uuid = result["entryUUID"].value obj = await udm.obj_by_dn(dn) assert obj.dn == dn assert obj._udm_module.name == object_type assert obj.uuid == uuid with ldap_connection(connection_kwargs={"read_only": True}) as conn: logger.info("Successful LDAP login.") conn.search( search_base=base_dn, search_filter="(&" "(univentionObjectType=*)" "(!(univentionObjectFlag=functional))" ")", attributes=[ "univentionObjectType", "univentionObjectFlag", "entryUUID" ], ) all_objs = {} async with UDM(**udm_kwargs) as udm: # test one object per udm module for result in conn.entries: object_type = result["univentionObjectType"].value if (object_type not in BAD_MODULE_NAMES and "://" not in result.entry_dn): # Bug #50175 all_objs.setdefault(object_type, []).append(result) module_names = all_objs.keys() random.shuffle([str(m) for m in module_names]) logger.info("Reading %d objects of different UDM module types...", len(module_names)) objs = await asyncio.gather( *(load_obj_by_dn(udm, random.choice(all_objs[module_name])) for module_name in module_names)) with open("/tmp/objs", "a") as fp: for obj in objs: fp.write(f"{obj!r}\n")
async def test_modify_user(fake_user, user_created_via_http, udm_kwargs): old_user_dn, old_user_url, old_user_data = user_created_via_http() new_user_data = fake_user() async with UDM(**udm_kwargs) as udm: mod = udm.get("users/user") obj = await mod.get(old_user_dn) assert obj.dn == old_user_dn assert obj.uri == old_user_url assert obj.position == old_user_data["position"] assert obj.props.firstname == old_user_data["properties"]["firstname"] assert obj.props.lastname == old_user_data["properties"]["lastname"] assert obj.props.birthday == old_user_data["properties"]["birthday"] obj.policies = new_user_data.policies modify_props = attr.asdict(new_user_data.props) del modify_props["username"] # not testing move here for k, v in modify_props.items(): setattr(obj.props, k, v) res = await obj.save() assert res is obj assert obj.dn == old_user_dn assert obj.uri == old_user_url policies = { "policies_desktop": [], "policies_pwhistory": [], "policies_umc": [], } assert obj.policies == policies for k, v in modify_props.items(): if k == "password": v = None assert getattr(obj.props, k) == v obj_new = await mod.get(old_user_dn) assert obj_new.dn == old_user_dn assert obj_new.uri == old_user_url assert obj_new.policies == policies for k, v in modify_props.items(): if k == "password": v = None assert getattr(obj_new.props, k) == v