async def test_delete(kelvin_session_kwargs, new_school_user): user = await new_school_user() async with Session(**kelvin_session_kwargs) as session: obj = await UserResource(session=session).get(school=user.school, name=user.name) assert obj res = await obj.delete() assert res is None async with Session(**kelvin_session_kwargs) as session: with pytest.raises(NoObject): await UserResource(session=session).get(school=user.school, name=user.name)
async def test_delete(kelvin_session_kwargs, new_school_class): sc1_dn, sc1_attr = await new_school_class() school = sc1_attr["school"] name = sc1_attr["name"] async with Session(**kelvin_session_kwargs) as session: obj = await SchoolClassResource(session=session).get(school=school, name=name) assert obj res = await obj.delete() assert res is None async with Session(**kelvin_session_kwargs) as session: with pytest.raises(NoObject): await SchoolClassResource(session=session).get(school=school, name=name)
async def test_search_no_name_arg( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_class, ): school = new_school(1)[0] school_name = school.name sc1_dn, sc1_attr = await new_school_class(school=school_name) sc2_dn, sc2_attr = await new_school_class(school=school_name) async with Session(**kelvin_session_kwargs) as session: objs = [ obj async for obj in SchoolClassResource(session=session).search( school=school_name) ] assert objs, f"No SchoolClass in school {school!r} found." assert len(objs) >= 2 assert sc1_dn in [sc.dn for sc in objs] assert sc2_dn in [sc.dn for sc in objs] for obj in objs: if obj.dn == sc1_dn: compare_kelvin_obj_with_test_data(obj, **sc1_attr) if obj.dn == sc2_dn: compare_kelvin_obj_with_test_data(obj, **sc2_attr)
async def test_search_partial_name_arg( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school, ): school = new_school(1)[0] name_len = len(school.name) name_begin = school.name[:int(name_len / 2)] name_end = school.name[len(name_begin):] async with Session(**kelvin_session_kwargs) as session: objs1 = [ obj async for obj in SchoolResource(session=session).search( name=f"{name_begin}*") ] objs2 = [ obj async for obj in SchoolResource(session=session).search( name=f"*{name_end}") ] assert objs1, f"No School for name='{name_begin}*' found." assert len(objs1) >= 1 assert school.dn in [o.dn for o in objs1] assert objs2, f"No School for name='*{name_end}' found." assert len(objs2) >= 1 assert school.dn in [o.dn for o in objs2]
async def test_search_partial_name_arg( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_class, ): school = new_school(1)[0] school_name = school.name # don't use usual name starting with 'test.', as leftovers of previous # tests will also match 'test.*' name = f"{fake.first_name()}{fake.first_name()}" sc1_dn, sc1_attr = await new_school_class(school=school_name, name=name) name_len = len(name) name_begin = name[:int(name_len / 2)] name_end = name[len(name_begin):] async with Session(**kelvin_session_kwargs) as session: objs1 = [ obj async for obj in SchoolClassResource(session=session).search( school=school_name, name=f"{name_begin}*") ] objs2 = [ obj async for obj in SchoolClassResource(session=session).search( school=school_name, name=f"*{name_end}") ] assert objs1, f"No SchoolClass for school={school_name!r} and name='{name_begin}*' found." assert len(objs1) == 1 assert sc1_dn == objs1[0].dn assert objs2, f"No SchoolClass for school={school_name!r} and name='*{name_end}' found." assert len(objs2) == 1 assert sc1_dn == objs2[0].dn
def kelvin_client_session(school_authority: SchoolAuthorityConfiguration, plugin_name: str) -> Session: m: Match = kelvin_url_regex().match(school_authority.url) if not m: raise ValueError( f"Bad Kelvin URL in school authority {school_authority!r}: {school_authority.url!r}." ) host = m.groupdict()["host"] try: username = school_authority.plugin_configs[plugin_name]["username"] password = school_authority.plugin_configs[plugin_name][ "password"].get_secret_value() except KeyError as exc: raise ValueError( f"Missing {exc!s} in Kelvin plugin configuration of school authority " f"{school_authority.dict()!r}.") timeout = httpx.Timeout(timeout=HTTP_REQUEST_TIMEOUT) certificate_path = fetch_ucs_certificate(host) ssl_context: ssl.SSLContext = httpx.create_ssl_context( verify=str(certificate_path)) for k, v in school_authority.plugin_configs[plugin_name].get( "ssl_context", {}).items(): logger.info("Applying to SSL context: %r=%r", k, v) setattr(ssl_context, k, v) return Session( username=username, password=password, host=host, verify=ssl_context, timeout=timeout, )
def _func(host: str, **kwargs) -> Session: if host not in sessions: session_kwargs = kelvin_session_kwargs(host) session_kwargs.update(kwargs) sessions[host] = Session(**session_kwargs) sessions[host].open() return sessions[host]
async def test_create_all_attrs( base_dn, compare_kelvin_obj_with_test_data, kelvin_session_kwargs, ldap_access, new_school_test_obj, schedule_delete_ou_using_ssh, ): school_data = new_school_test_obj() schedule_delete_ou_using_ssh(school_data.name) async with Session(**kelvin_session_kwargs) as session: school_kwargs = asdict(school_data) print(f"Creating school with kwargs: {school_kwargs!r}") school_obj = School(session=session, **school_kwargs) await school_obj.save() print("Created new School: {!r}".format(school_obj.as_dict())) ldap_filter = f"(&(ou={school_obj.name})(objectClass=ucsschoolOrganizationalUnit))" ldap_objs = await ldap_access.search(filter_s=ldap_filter) assert len(ldap_objs) == 1 ldap_obj = ldap_objs[0] assert ldap_obj.entry_dn == school_obj.dn assert ldap_obj["ou"].value == school_obj.name assert ldap_obj[ "ucsschoolRole"].value == f"school:school:{school_obj.name}" assert ldap_obj["displayName"].value == school_obj.display_name dc_base_dn = f"cn=dc,cn=server,cn=computers,ou={school_obj.name},{base_dn}" class_share_file_server_dn_dn = f"cn={school_obj.class_share_file_server},{dc_base_dn}" home_share_file_server_dn = f"cn={school_obj.home_share_file_server},{dc_base_dn}" assert ldap_obj[ "ucsschoolClassShareFileServer"].value == class_share_file_server_dn_dn assert ldap_obj[ "ucsschoolHomeShareFileServer"].value == home_share_file_server_dn
async def test_create( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, ldap_access, new_school_class_test_obj, schedule_delete_obj, new_school_user, ): sc_data = new_school_class_test_obj() user1 = await new_school_user(school=sc_data.school) user2 = await new_school_user(school=sc_data.school) sc_data.users = [user1.name, user2.name] async with Session(**kelvin_session_kwargs) as session: sc_kwargs = asdict(sc_data) sc_obj = SchoolClass(session=session, **sc_kwargs) schedule_delete_obj(object_type="class", school=sc_data.school, name=sc_data.name) await sc_obj.save() print("Created new SchoolClass: {!r}".format(sc_obj.as_dict())) ldap_filter = f"(&(cn={sc_data.school}-{sc_data.name})(objectClass=ucsschoolGroup))" ldap_objs = await ldap_access.search(filter_s=ldap_filter) assert len(ldap_objs) == 1 ldap_obj = ldap_objs[0] assert ldap_obj.entry_dn == sc_obj.dn assert ldap_obj["cn"].value == f"{sc_data.school}-{sc_data.name}" assert ldap_obj[ "ucsschoolRole"].value == f"school_class:school:{sc_data.school}" assert ldap_obj["description"].value == sc_obj.description assert set(ldap_obj["uniqueMember"].value) == {user1.dn, user2.dn}
async def test_search_partial_name_arg( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_user, ): school = new_school(1)[0] user = await new_school_user(school=school.name) name = user.name name_len = len(name) name_begin = name[: int(name_len / 2)] name_end = name[len(name_begin) :] async with Session(**kelvin_session_kwargs) as session: objs1 = [ obj async for obj in UserResource(session=session).search( school=school.name, name=f"{name_begin}*" ) ] objs2 = [ obj async for obj in UserResource(session=session).search( school=school.name, name=f"*{name_end}" ) ] assert objs1, f"No User for school={school.name!r} and name='{name_begin}*' found." assert len(objs1) == 1 assert user.dn == objs1[0].dn assert objs2, f"No User for school={school.name!r} and name='*{name_end}' found." assert len(objs2) == 1 assert user.dn == objs2[0].dn
async def test_move_change_name( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school_user, schedule_delete_obj, ): user = await new_school_user() old_name = user.name new_name = fake.first_name() assert old_name != new_name async with Session(**kelvin_session_kwargs) as session: user_resource = UserResource(session=session) obj: User = await user_resource.get(school=user.school, name=old_name) assert obj.name == old_name compare_kelvin_obj_with_test_data(obj, **asdict(user)) obj.name = new_name old_url = obj.url schedule_delete_obj(object_type="user", name=new_name) new_obj: User = await obj.save() assert new_obj is obj assert new_obj.name == new_name assert f"uid={new_name}" in new_obj.dn assert new_obj.url != old_url # load fresh object fresh_obj: User = await user_resource.get(school=user.school, name=new_name) assert fresh_obj.name == new_name assert fresh_obj.url != old_url compare_kelvin_obj_with_test_data(fresh_obj, **new_obj.as_dict())
async def test_modify( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school_class, new_school_class_test_obj, ): sc1_dn, sc1_attr = await new_school_class() new_data = asdict(new_school_class_test_obj()) school = sc1_attr["school"] name = sc1_attr["name"] async with Session(**kelvin_session_kwargs) as session: sc_resource = SchoolClassResource(session=session) obj: SchoolClass = await sc_resource.get(school=school, name=name) compare_kelvin_obj_with_test_data(obj, dn=sc1_dn, **sc1_attr) for k, v in new_data.items(): if k not in ("school", "name", "dn", "url", "ucsschool_roles"): setattr(obj, k, v) new_obj: SchoolClass = await obj.save() assert new_obj is obj assert new_obj.as_dict() == obj.as_dict() # load fresh object fresh_obj: SchoolClass = await sc_resource.get(school=school, name=name) assert fresh_obj.as_dict() == new_obj.as_dict() compare_kelvin_obj_with_test_data(fresh_obj, **obj.as_dict())
async def test_move_change_name( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school_class, schedule_delete_obj, ): sc1_dn, sc1_attr = await new_school_class() school = sc1_attr["school"] old_name = sc1_attr["name"] new_name = fake.first_name() assert old_name != new_name async with Session(**kelvin_session_kwargs) as session: sc_resource = SchoolClassResource(session=session) obj: SchoolClass = await sc_resource.get(school=school, name=old_name) assert obj.name == old_name compare_kelvin_obj_with_test_data(obj, dn=sc1_dn, **sc1_attr) obj.name = new_name old_url = obj.url schedule_delete_obj(object_type="class", school=school, name=new_name) new_obj: SchoolClass = await obj.save() assert new_obj is obj assert new_obj.name == new_name assert new_obj.url != old_url # load fresh object fresh_obj: SchoolClass = await sc_resource.get(school=school, name=new_name) assert fresh_obj.name == new_name assert fresh_obj.url != old_url compare_kelvin_obj_with_test_data(fresh_obj, **new_obj.as_dict())
async def test_modify( check_password, compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school_user, new_user_test_obj, ): user = await new_school_user() new_data = asdict( new_user_test_obj( name=user.name, roles=user.roles, school=user.school, schools=user.schools, ucsschool_roles=user.ucsschool_roles, ) ) async with Session(**kelvin_session_kwargs) as session: user_resource = UserResource(session=session) obj: User = await user_resource.get(school=user.school, name=user.name) compare_kelvin_obj_with_test_data(obj, **asdict(user)) await check_password(obj.dn, user.password) for k, v in new_data.items(): if k not in ("dn", "url"): setattr(obj, k, v) new_obj: User = await obj.save() assert new_obj is obj compare_kelvin_obj_with_test_data(new_obj, **obj.as_dict()) # load fresh object fresh_obj: User = await user_resource.get(school=user.school, name=user.name) compare_kelvin_obj_with_test_data(fresh_obj, **new_obj.as_dict()) compare_kelvin_obj_with_test_data(fresh_obj, **obj.as_dict()) await check_password(fresh_obj.dn, new_data["password"])
async def test_move_change_school( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_class, schedule_delete_obj, ): sc1_dn, sc1_attr = await new_school_class() old_school = sc1_attr["school"] school1, school2 = new_school(2) ou1, ou2 = school1.name, school2.name name = sc1_attr["name"] new_school_ = ou1 if old_school == ou2 else ou2 assert old_school != new_school_ async with Session(**kelvin_session_kwargs) as session: sc_resource = SchoolClassResource(session=session) obj: SchoolClass = await sc_resource.get(school=old_school, name=name) assert obj.school == old_school compare_kelvin_obj_with_test_data(obj, dn=sc1_dn, **sc1_attr) obj.school = new_school_ schedule_delete_obj(object_type="class", school=new_school_, name=name) with pytest.raises(InvalidRequest) as exc_info: await obj.save() assert "Moving of class to other school is not allowed" in exc_info.value.args[ 0]
async def test_role_attrs(compare_kelvin_obj_with_test_data, kelvin_session_kwargs): role = random.choice(("staff", "student", "teacher")) async with Session(**kelvin_session_kwargs) as session: obj = await RoleResource(session=session).get(name=role) assert obj.name == role assert set(obj._kelvin_attrs) == {"name", "display_name"} assert set(obj.as_dict().keys()) == {"name", "display_name", "url"}
async def test_get_from_url(compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school): school = new_school(1)[0] url = URL_SCHOOL_OBJECT.format(host=kelvin_session_kwargs["host"], name=school.name) async with Session(**kelvin_session_kwargs) as session: obj = await SchoolResource(session=session).get_from_url(url) compare_kelvin_obj_with_test_data(obj, **asdict(school))
async def test_search_inexact_school(new_school, kelvin_session_kwargs): school = new_school(1)[0] school_name = school.name school_name_len = len(school_name) school_name_begin = school_name[: int(school_name_len / 2)] async with Session(**kelvin_session_kwargs) as session: with pytest.raises(InvalidRequest): assert [ obj async for obj in UserResource(session=session).search(school=f"{school_name_begin}*") ]
async def test_search_no_name_arg(compare_kelvin_obj_with_test_data, kelvin_session_kwargs): async with Session(**kelvin_session_kwargs) as session: objs = [obj async for obj in RoleResource(session=session).search()] assert objs, "No roles found." assert len(objs) == 3 for obj in objs: assert not hasattr(obj, "dn") assert not hasattr(obj, "ucsschool_roles") assert {"staff", "student", "teacher"} == {obj.name for obj in objs}
async def test_get_from_url(compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school_class): sc1_dn, sc1_attr = await new_school_class() school = sc1_attr["school"] name = sc1_attr["name"] url = URL_CLASS_OBJECT.format(host=kelvin_session_kwargs["host"], school=school, name=name) async with Session(**kelvin_session_kwargs) as session: obj = await SchoolClassResource(session=session).get_from_url(url) compare_kelvin_obj_with_test_data(obj, dn=sc1_dn, **sc1_attr)
async def test_get_no_users( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school, new_school_class, ): sc_dn, sc_attr = await new_school_class() async with Session(**kelvin_session_kwargs) as session: obj = await SchoolClassResource(session=session ).get(school=sc_attr["school"], name=sc_attr["name"]) compare_kelvin_obj_with_test_data(obj, dn=sc_dn, **sc_attr)
async def test_search_no_name_arg(compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs): school1, school2 = new_school(2) async with Session(**kelvin_session_kwargs) as session: objs = [obj async for obj in SchoolResource(session=session).search()] assert objs, "No Schools found." assert len(objs) >= 2 assert school1.dn in [school.dn for school in objs] assert school2.dn in [school.dn for school in objs] for obj in objs: if obj.dn == school1.dn: compare_kelvin_obj_with_test_data(obj, **asdict(school1)) if obj.dn == school2.dn: compare_kelvin_obj_with_test_data(obj, **asdict(school2))
async def test_get_with_users( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, new_school, new_school_class, new_school_user, ): user1 = await new_school_user() user2 = await new_school_user(school=user1.school) sc_dn, sc_attr = await new_school_class(school=user1.school, users=[user1.name, user2.name]) async with Session(**kelvin_session_kwargs) as session: obj = await SchoolClassResource(session=session ).get(school=sc_attr["school"], name=sc_attr["name"]) assert set(obj.users) == {user1.name, user2.name} compare_kelvin_obj_with_test_data(obj, dn=sc_dn, **sc_attr)
async def test_modify_password_hashes( check_password, kelvin_session_kwargs, password_hash, new_school_user, ): user = await new_school_user() password, password_hashes = await password_hash() assert user.password != password async with Session(**kelvin_session_kwargs) as session: user_resource = UserResource(session=session) obj: User = await user_resource.get(school=user.school, name=user.name) await check_password(obj.dn, user.password) obj.password = None obj.kelvin_password_hashes = PasswordsHashes(**asdict(password_hashes)) await obj.save() fresh_obj: User = await user_resource.get(school=user.school, name=user.name) await check_password(fresh_obj.dn, password)
async def test_reload( compare_kelvin_obj_with_test_data, kelvin_session_kwargs, ldap_access, new_school_user, ): user = await new_school_user() first_name_old = user.firstname first_name_new = fake.first_name() async with Session(**kelvin_session_kwargs) as session: obj: User = await UserResource(session=session).get(school=user.school, name=user.name) assert obj.firstname == first_name_old await obj.reload() assert obj.firstname == first_name_old await ldap_access.modify(obj.dn, {"givenName": [(ldap3.MODIFY_REPLACE, [first_name_new])]}) await obj.reload() assert obj.firstname == first_name_new
async def test_create_with_password_hashes( check_password, kelvin_session_kwargs, password_hash, new_user_test_obj, schedule_delete_obj, ): user_data = new_user_test_obj() password, password_hashes = await password_hash() assert user_data.password != password user_data.password = None user_data.kelvin_password_hashes = PasswordsHashes(**asdict(password_hashes)) async with Session(**kelvin_session_kwargs) as session: user_obj = User(session=session, **asdict(user_data)) schedule_delete_obj(object_type="user", name=user_data.name) await user_obj.save() print("Created new User: {!r}".format(user_obj.as_dict())) await check_password(user_obj.dn, password)
async def test_reload(compare_kelvin_obj_with_test_data, kelvin_session_kwargs, ldap_access, new_school): school = new_school(1)[0] display_name_old = school.display_name display_name_new = fake.text(max_nb_chars=50) async with Session(**kelvin_session_kwargs) as session: obj: School = await SchoolResource(session=session ).get(name=school.name) assert obj.display_name == display_name_old await obj.reload() assert obj.display_name == display_name_old await ldap_access.modify( obj.dn, {"displayName": [(ldap3.MODIFY_REPLACE, [display_name_new])]}) await obj.reload() assert obj.display_name == display_name_new await ldap_access.modify( obj.dn, {"displayName": [(ldap3.MODIFY_REPLACE, [display_name_old])]})
async def test_search_exact( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_user, attr, ): user = await new_school_user() value = getattr(user, attr) async with Session(**kelvin_session_kwargs) as session: objs = [ obj async for obj in UserResource(session=session).search(school=user.school, **{attr: value}) ] assert objs, f"No User for school={user.school!r} and {attr!r}={value!r} found." if attr in ("roles", "disabled"): assert len(objs) >= 1 assert user.dn in [o.dn for o in objs] else: assert len(objs) == 1 assert user.dn == objs[0].dn
async def test_search_no_name_arg( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_user, ): school = new_school(1)[0] user1 = await new_school_user(school=school.name) user2 = await new_school_user(school=school.name) async with Session(**kelvin_session_kwargs) as session: objs = [obj async for obj in UserResource(session=session).search(school=school.name)] assert objs, f"No Users in school {school.name!r} found." assert len(objs) >= 2 assert user1.dn in [obj.dn for obj in objs] assert user2.dn in [obj.dn for obj in objs] for obj in objs: if obj.dn == user1.dn: compare_kelvin_obj_with_test_data(obj, **asdict(user1)) elif obj.dn == user2.dn: compare_kelvin_obj_with_test_data(obj, **asdict(user2))
async def test_search_inexact( compare_kelvin_obj_with_test_data, new_school, kelvin_session_kwargs, new_school_user, attr, ): user = await new_school_user() value = getattr(user, attr) value_len = len(value) value_begin = value[: int(value_len / 2)] value_end = value[len(value_begin) :] async with Session(**kelvin_session_kwargs) as session: objs1 = [ obj async for obj in UserResource(session=session).search( school=user.school, **{attr: f"{value_begin}*"} ) ] objs2 = [ obj async for obj in UserResource(session=session).search( school=user.school, **{attr: f"*{value_end}"} ) ] assert objs1, f"No User for school={user.school!r} and {attr!r}='{value_begin}*' found." assert objs2, f"No User for school={user.school!r} and {attr!r}='*{value_end}' found." if attr == "source_uid": assert len(objs1) >= 1 assert user.dn in [o.dn for o in objs1] assert len(objs2) >= 1 assert user.dn in [o.dn for o in objs2] else: assert len(objs1) == 1 assert user.dn == objs1[0].dn assert len(objs2) == 1 assert user.dn == objs2[0].dn