from unittest.mock import patch, Mock
from timApp import tim_celery
from timApp.auth.accesstype import AccessType
from timApp.document.docentry import DocEntry
from timApp.folder.folder import Folder
from timApp.tests.server.timroutetest import TimRouteTest
from timApp.tim_app import app
from timApp.timdb.sqa import db
from timApp.user.user import User, UserInfo
from timApp.user.usergroup import UserGroup
[docs]class GroupTest(TimRouteTest):
[docs] def error_resp(self, name):
return {
"error": "User group must contain at least one digit and one letter and must "
'not have uppercase or special chars: "' + name + '"'
}
[docs] def enum_admin_and_groupadmin(self):
yield self.init_admin()
yield self.init_groupadmin()
[docs] def test_groups(self):
for is_admin in self.enum_admin_and_groupadmin():
names = [f"t{i}{is_admin}" for i in range(1, 5)]
t1 = names[0]
t2 = names[1]
t3 = names[2]
t4 = names[3]
t5 = f"t5{is_admin}"
users_and_groups = [
User.create_with_group(
UserInfo(username=name, full_name=name, email=name + "@example.com")
)
for name in names
]
db.session.flush()
t1gid = users_and_groups[0][1].id
uids = [u.id for u, g in users_and_groups]
db.session.commit()
groupname = f"testgroup1{str(is_admin).lower()}"
self.get(
f"/groups/show/{groupname}",
expect_status=400,
expect_content={"error": f'User group "{groupname}" not found'},
)
self.get(f"/groups/create/{groupname}")
self.get(
f"/groups/create/{groupname}",
expect_content={"error": "User group already exists."},
expect_status=400,
)
self.json_post(
f"/groups/addmember/{groupname}",
{"names": f"{t1},{t3}".split(",")},
expect_content={
"added": [t1, t3],
"already_belongs": [],
"not_exist": [],
},
)
self.json_post(
f"/groups/addmember/{groupname}",
{"names": f"{t1},{t3}".split(",")},
expect_content={
"already_belongs": [t1, t3],
"added": [],
"not_exist": [],
},
)
self.json_post(
f"/groups/addmember/{groupname}",
{"names": f"{t1},{t2},{t3},{t4},{t5}".split(",")},
expect_content={
"already_belongs": [t1, t3],
"added": [t2, t4],
"not_exist": [t5],
},
)
self.json_post(
f"/groups/addmember/{groupname}",
{"names": f"{t1},{t2}".split(",")},
expect_content={
"already_belongs": [t1, t2],
"added": [],
"not_exist": [],
},
)
ug = UserGroup.get_by_name(groupname)
if is_admin:
self.get(
f"/groups/usergroups/{t1}",
expect_content=[
{"id": t1gid, "name": t1},
{"id": ug.id, "name": groupname},
],
)
self.get(
f"/groups/belongs/{t1}/{groupname}", expect_content={"status": True}
)
else:
self.get(
f"/groups/usergroups/{t1}",
expect_status=403,
expect_content="This action requires administrative rights.",
)
self.get(
f"/groups/belongs/{t1}/{groupname}",
)
self.get(
f"/groups/show/{groupname}",
expect_content=[
{
"email": t1 + "@example.com",
"id": uids[0],
"name": names[0],
"real_name": names[0],
},
{
"email": t2 + "@example.com",
"id": uids[1],
"name": names[1],
"real_name": names[1],
},
{
"email": t3 + "@example.com",
"id": uids[2],
"name": names[2],
"real_name": names[2],
},
{
"email": t4 + "@example.com",
"id": uids[3],
"name": names[3],
"real_name": names[3],
},
],
)
self.json_post(
f"/groups/removemember/{groupname}",
{"names": f"{t1},{t3}".split(",")},
expect_content={
"removed": [t1, t3],
"does_not_belong": [],
"not_exist": [],
},
)
self.json_post(
f"/groups/removemember/{groupname}",
{"names": f"{t1},{t3}".split(",")},
expect_content={
"removed": [],
"does_not_belong": [t1, t3],
"not_exist": [],
},
)
self.json_post(
f"/groups/removemember/{groupname}",
{"names": f"{t1},{t2},{t3},{t4},{t5}".split(",")},
expect_content={
"removed": [t2, t4],
"does_not_belong": [t1, t3],
"not_exist": [t5],
},
)
self.get(f"/groups/show/{groupname}", expect_content=[])
[docs] def init_admin(self):
u = self.test_user_3
self.make_admin(u)
self.login_test3()
return True
[docs] def init_groupadmin(self):
u = self.test_user_2
u.add_to_group(UserGroup.get_groupadmin_group(), added_by=None)
db.session.commit()
self.login_test2()
return False
[docs] def test_invalid_groups(self):
for is_admin in self.enum_admin_and_groupadmin():
is_admin_postfix = str(is_admin).lower()
self.get(
"/groups/create/testgroup",
expect_status=400,
expect_content=self.error_resp("testgroup"),
)
self.get(
"/groups/create/1",
expect_status=400,
expect_content=self.error_resp("1"),
)
self.get(
"/groups/create/a1@a",
expect_status=400,
expect_content=self.error_resp("a1@a"),
)
self.get(
"/groups/create/ok ok",
expect_status=400,
expect_content=self.error_resp("ok ok"),
)
self.get(
"/groups/create/TestGroup1",
expect_status=400,
expect_content=self.error_resp("TestGroup1"),
)
self.get(f"/groups/create/test x1{is_admin_postfix}")
self.json_post(
"/groups/addmember/Logged-in users",
{"names": f"testuser1".split(",")},
expect_status=400,
expect_content={"error": "Cannot edit special group: Logged-in users"},
)
if not is_admin:
self.json_post(
"/groups/addmember/Group admins",
{"names": f"testuser1".split(",")},
expect_status=403,
expect_content={
"error": "This action requires administrative rights."
},
)
self.json_post(
"/groups/addmember/Administrators",
{"names": f"testuser1".split(",")},
expect_status=403,
expect_content={
"error": "This action requires administrative rights."
},
)
self.json_post(
"/groups/removemember/Administrators",
{"names": f"testuser1".split(",")},
expect_status=403,
expect_content={
"error": "This action requires administrative rights."
},
)
else:
self.json_post(
"/groups/addmember/Group admins", {"names": f"testuser1".split(",")}
)
self.json_post(
"/groups/addmember/testuser1",
{"names": f"testuser2".split(",")},
expect_status=400,
expect_content={"error": "Cannot edit personal group: testuser1"},
)
self.json_post(
"/groups/removemember/testuser1",
{"names": f"testuser1".split(",")},
expect_status=400,
expect_content={"error": "Cannot edit personal group: testuser1"},
)
self.json_post(
"/groups/removemember/Logged-in users",
{"names": f"testuser1".split(",")},
expect_status=400,
expect_content={"error": "Cannot edit special group: Logged-in users"},
)
self.json_post(
f"/groups/addmember/test x1{is_admin_postfix}",
{"names": ["Anonymous"]},
expect_status=400,
expect_content={"error": "Cannot add special users."},
)
self.json_post(
f"/groups/addmember/test x1{is_admin_postfix}",
{"names": ["Logged-in user"]},
expect_status=400,
expect_content={"error": "Cannot add special users."},
)
[docs] def test_nonexistent(self):
self.init_admin()
self.get(
f"/groups/belongs/asd/testuser1",
expect_content={"error": "User not found"},
expect_status=404,
)
self.get(
f"/groups/belongs/testuser1/asd",
expect_content={"error": 'User group "asd" not found'},
expect_status=400,
)
self.get(
f"/groups/usergroups/asd",
expect_content={"error": "User not found"},
expect_status=404,
)
self.json_post(
f"/groups/addmember/asd",
{"names": f"testuser1".split(",")},
expect_content={"error": 'User group "asd" not found'},
expect_status=400,
)
[docs] def test_groups_trim(self):
self.init_admin()
self.get("/groups/create/testing1")
self.json_post(
"/groups/addmember/testing1",
{"names": f"testuser1 ,testuser2 ".split(",")},
expect_content={
"added": ["testuser1", "testuser2"],
"already_belongs": [],
"not_exist": [],
},
)
[docs] def test_invalid_group_setting(self):
d = self.create_doc(settings={"group": ["a", "b"]})
html = self.get(d.get_url_for_view("teacher"))
self.assertIn("The setting 'group' must be a string.", html)
[docs] def test_doc_group_setting_access(self):
self.login_test1()
no_access_msg = "You don't have access to group 'testuser1'."
d = self.create_doc(
settings={"group": self.current_user.get_personal_group().name}
)
self.assertNotIn(no_access_msg, self.get(d.get_url_for_view("teacher")))
self.test_user_2.grant_access(d, AccessType.teacher)
db.session.commit()
self.login_test2()
self.assertIn(no_access_msg, self.get(d.get_url_for_view("teacher")))
[docs] def test_group_member_sync(self):
self.login_test1()
UserGroup.get_or_create_group("test_group_sync")
db.session.commit()
with self.temp_config(
{
"SYNC_USER_GROUPS_SEND_SECRET": "xxx",
"SYNC_USER_GROUPS_RECEIVE_SECRET": "xxx",
"SYNC_USER_GROUPS_HOSTS": [
f'http://{app.config["INTERNAL_PLUGIN_DOMAIN"]}:5001'
],
}
):
with patch.object(
tim_celery.sync_user_group_memberships,
"delay",
wraps=tim_celery.do_send_user_group_info,
) as m: # type: Mock
with self.internal_container_ctx():
tim_celery.sync_user_group_memberships.delay(
self.test_user_1.email,
[ug.name for ug in self.test_user_1.groups]
+ ["test_group_sync"],
)
db.session.commit()
self.assertEqual(
{
u.name
for u in UserGroup.get_by_name("test_group_sync").users
},
{self.test_user_1.name},
)
tim_celery.sync_user_group_memberships.delay(
self.test_user_1.email,
[
ug.name
for ug in self.test_user_1.groups
if ug.name != "test_group_sync"
],
)
db.session.commit()
self.assertEqual(
len(
[
u.name
for u in UserGroup.get_by_name("test_group_sync").users
]
),
0,
)
self.assertEqual(2, m.call_count)
[docs]class GroupTest2(TimRouteTest):
[docs] def test_group_edit_access(self):
self.test_user_3.make_admin()
db.session.commit()
self.login_test3()
self.get("/groups/create/edittest1")
d = DocEntry.find_by_path("groups/edittest1")
self.assertEqual("Administrators", d.parent.owners[0].name)
self.assertEqual(
{"group": "edittest1", "fields": ["info"], "maxRows": "40em"},
d.document.get_settings().get_dict()["macros"],
)
self.login_test1()
self.get("/groups/show/edittest1", expect_status=403)
self.test_user_1.grant_access(d, AccessType.view)
db.session.commit()
self.get(d.url)
self.get(d.parent.url)
self.get("/groups/show/edittest1")
self.json_post(
"/groups/addmember/edittest1",
{"names": f"testuser1".split(",")},
expect_status=403,
)
self.test_user_1.grant_access(d, AccessType.edit)
db.session.commit()
self.json_post(
"/groups/addmember/edittest1",
{"names": f"testuser1".split(",")},
expect_content={
"added": ["testuser1"],
"already_belongs": [],
"not_exist": [],
},
)
[docs]class GroupTest3(TimRouteTest):
[docs] def test_create_group(self):
self.login_test1() # This was needed to create user1 with group.
# Test user 1:
# Create a user with group administrator rights and login.
# Luodaan testikäyttäjä ryhmänhallinnan oikeuksilla ja kirjataan sisään.
user1, _ = User.create_with_group(
UserInfo(username="user1", email="user1@jyu.fi")
)
user1.add_to_group(UserGroup.get_groupadmin_group(), added_by=None)
db.session.commit()
self.login(username=user1.name)
# Test case 1:
# Users should not have a right to create new subdirectories directly to the groups root folder.
# Ei pitäisi olla oikeutta luoda käyttäjäryhmiä suoraan juurihakemiston (groups) alihakemistoon.
self.get(
"/groups/create/kurssit/tie/ohj2/2022k",
expect_status=403,
expect_content={"error": "You cannot create documents in this folder."},
)
# Test case 2:
# Users are able to create user groups to the subdirectories they already have manage access rights to.
# Käyttäjät voivat luoda käyttäjäryhmiä juurihakemiston (groups) alihakemistoon, jos siihen on oikeus.
Folder.create(
"groups/kurssit/tie/ohj2/2022k/tentit", user1.get_personal_group()
)
db.session.commit()
new_group = self.get(
"/groups/create/kurssit/tie/ohj2/2022k/tentit/ohj2_valikoe_zoom"
)
self.assertEqual(
new_group["path"],
"groups/kurssit/tie/ohj2/2022k/tentit/ohj2_valikoe_zoom",
)
existing_folder = Folder.find_by_path("groups/kurssit/tie/ohj2/2022k/tentit")
self.assertIn(
user1.get_personal_group().name,
[group.name for group in existing_folder.owners],
)
# Test case 3:
# Users are able to create new subdirectories to the directories they have manage access rights to.
# Käyttäjät voivat luoda hakemistoon uusia alihakemistoja ja saavat niihin omistusoikeuden.
self.get("/groups/create/tie/ohj2/2021s/ohj2_ohjaajat")
new_folder = Folder.find_by_path("groups/tie/ohj2/2021s")
self.assertIn(
user1.get_personal_group().name, [group.name for group in new_folder.owners]
)
# Test user 2:
# Create a user with group administrator rights and login.
# Luodaan testikäyttäjä ryhmänhallinnan oikeuksilla ja kirjataan sisään.
user2, _ = User.create_with_group(
UserInfo(username="user2", email="user2@jyu.fi"),
)
user2.add_to_group(UserGroup.get_groupadmin_group(), added_by=None)
db.session.commit()
self.login(username=user2.name)
# Test case 4:
# Another user may not create a group in a subdirectory to which user has no rights.
# Toinen käyttäjä ei saa luoda ryhmää alihakemistoon, johon hänellä ei ole oikeutta.
self.get(
"/groups/create/kurssit/tie/ohj2/2021s",
expect_status=403,
expect_content={"error": "You cannot create documents in this folder."},
)