Source code for timApp.tests.server.test_permissions

from datetime import timedelta

from timApp.auth.accesstype import AccessType
from timApp.document.docentry import DocEntry
from timApp.document.editing.documenteditresult import DocumentEditResult
from timApp.document.translation.synchronize_translations import (
    synchronize_translations,
)
from timApp.item.item import Item
from timApp.tests.server.test_default_rights import convert_to_old_format
from timApp.tests.server.timroutetest import TimRouteTest
from timApp.timdb.sqa import db
from timApp.user.usergroup import UserGroup
from timApp.user.userutils import grant_access
from timApp.util.utils import get_current_time


[docs]class PermissionTest(TimRouteTest):
[docs] def test_cannot_remove_ownership(self): self.login_test1() d = self.create_doc() self.assertTrue(self.current_user.has_ownership(d)) self.json_put( f"/permissions/add", { "time": { "from": get_current_time() + timedelta(days=1), "type": "range", }, "id": d.id, "type": AccessType.owner.value, "groups": ["testuser1"], "confirm": False, }, expect_status=403, expect_content={"error": "You cannot remove ownership from yourself."}, ) db.session.remove() d = DocEntry.find_by_id(d.id) self.assertTrue(self.current_user.has_ownership(d)) self.json_put( f"/permissions/remove", { "id": d.id, "type": AccessType.owner.value, "group": self.get_test_user_1_group_id(), }, expect_status=403, ) db.session.remove() d = DocEntry.find_by_id(d.id) self.assertTrue(self.current_user.has_ownership(d))
[docs] def test_non_owner_cannot_change_owner(self): self.login_test1() d = self.create_doc() docid = d.id self.test_user_2.grant_access(d, AccessType.manage) db.session.commit() self.login_test2() self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": docid, "type": AccessType.owner.value, "groups": ["testuser2"], "confirm": False, }, expect_status=403, )
[docs] def test_cannot_change_owner_of_personal_folder(self): self.login_test1() f = self.current_user.get_personal_folder() self.json_put( f"/permissions/add", { "id": f.id, "type": AccessType.owner.value, "groups": ["testuser2"], "time": { "type": "always", }, "confirm": False, }, expect_status=403, expect_content={"error": "You cannot add owners to your personal folder."}, )
[docs] def test_trim_whitespace(self): self.login_test1() f = self.current_user.get_personal_folder() self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": f.id, "type": AccessType.view.value, "groups": ["testuser2", "testuser3"], "confirm": False, }, ) f = self.current_user.get_personal_folder() self.assertTrue(self.test_user_2.has_view_access(f)) self.assertTrue(self.test_user_3.has_view_access(f))
[docs] def test_nonexistent_user(self): self.login_test1() f = self.create_doc() self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": f.id, "type": AccessType.view.value, "groups": ["testuser2", "testuserx"], "confirm": False, }, expect_content={"not_exist": ["testuserx"]}, ) self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": f.id, "type": AccessType.view.value, "groups": ["testuserx"], "confirm": False, }, expect_content={"not_exist": ["testuserx"]}, )
[docs] def test_permissions_get(self): self.login_test1() for i in (self.current_user.get_personal_folder(), self.create_doc()): self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": i.id, "type": AccessType.view.value, "groups": ["testuser2"], "confirm": False, }, ) rights = self.get(f"/permissions/get/{i.id}") i = Item.find_by_id(i.id) self.assertEqual( rights["accesstypes"], [ {"id": 1, "name": "view"}, {"id": 2, "name": "edit"}, {"id": 3, "name": "teacher"}, {"id": 4, "name": "manage"}, {"id": 5, "name": "see answers"}, {"id": 6, "name": "owner"}, {"id": 7, "name": "copy"}, ], ) self.assertEqual( convert_to_old_format(rights["grouprights"]), [ { "access_name": "owner", "access_type": 6, "accessible_from": i.block.accesses[ (self.get_test_user_1_group_id(), AccessType.owner.value) ].accessible_from.isoformat(), "accessible_to": None, "duration": None, "duration_from": None, "duration_to": None, "fullname": "Test user 1", "gid": self.get_test_user_1_group_id(), "name": "testuser1", }, { "access_name": "view", "access_type": 1, "accessible_from": str( i.block.accesses[ (self.get_test_user_2_group_id(), AccessType.view.value) ].accessible_from.isoformat() ), "accessible_to": None, "duration": None, "duration_from": None, "duration_to": None, "fullname": "Test user 2", "gid": self.get_test_user_2_group_id(), "name": "testuser2", }, ], )
[docs] def test_logged_in_right(self): self.login_test1() d = self.create_doc() self.json_put( f"/permissions/add", { "time": { "from": get_current_time(), "type": "always", }, "id": d.id, "type": AccessType.view.value, "groups": ["Logged-in users"], "confirm": False, }, ) self.login_test2() self.get(d.url) self.login_test1() self.json_put( f"/permissions/remove", { "id": d.id, "type": AccessType.view.value, "group": UserGroup.get_logged_in_group().id, }, ) self.login_test2() self.get(d.url, expect_status=403)
[docs] def test_recursive_permissions(self): self.login_test1() paths = [ "a", "b", "c", "x/a", "x/b", "x/c", "x/x/a", "x/x/b", "x/x/c", ] ds = [] for p in paths: d = self.create_doc(self.get_personal_item_path(p)) ds.append(d) t1_f = self.test_user_1.get_personal_folder() self.assertFalse(self.test_user_2.has_view_access(d)) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertFalse(self.test_user_2.has_edit_access(t1_f)) all_ids = [x.id for x in ds] self.json_put( f"/permissions/edit", { "groups": ["testuser1"], "type": AccessType.owner.value, "action": "remove", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, expect_status=403, ) self.json_put( f"/permissions/edit", { "groups": ["testuser2", "testuser3"], "type": AccessType.view.value, "action": "add", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, ) t1_f = self.test_user_1.get_personal_folder() for p in paths: d = DocEntry.find_by_path(self.get_personal_item_path(p)) self.assertTrue(self.test_user_2.has_view_access(d)) self.assertTrue(self.test_user_3.has_view_access(d)) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertFalse(self.test_user_2.has_edit_access(t1_f)) self.json_put( f"/permissions/edit", { "groups": ["testuser2"], "type": AccessType.view.value, "action": "remove", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, ) t1_f = self.test_user_1.get_personal_folder() for p in paths: d = DocEntry.find_by_path(self.get_personal_item_path(p)) self.assertTrue(self.test_user_1.has_ownership(d)) self.assertFalse(self.test_user_2.has_view_access(d)) self.assertTrue(self.test_user_3.has_view_access(d)) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertFalse(self.test_user_2.has_edit_access(t1_f)) self.json_put( f"/permissions/edit", { "groups": ["testuser2", "testuser3"], "type": AccessType.view.value, "action": "remove", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, ) t1_f = self.test_user_1.get_personal_folder() for p in paths: d = DocEntry.find_by_path(self.get_personal_item_path(p)) self.assertTrue(self.test_user_1.has_ownership(d)) self.assertFalse(self.test_user_2.has_view_access(d)) self.assertFalse(self.test_user_3.has_view_access(d)) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertFalse(self.test_user_2.has_edit_access(t1_f)) self.login_test2() self.create_doc() self.login_test1() self.json_put( f"/permissions/edit", { "groups": ["testuser1"], "type": AccessType.view.value, "action": "add", "ids": [ x.id for x in self.test_user_2.get_personal_folder().get_all_documents( include_subdirs=True ) ], "time": { "type": "always", }, "confirm": False, }, expect_status=403, ) self.json_put( f"/permissions/edit", { "groups": ["testuser1"], "type": "asd", "action": "add", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, expect_status=422, ) self.json_put( f"/permissions/edit", { "groups": ["nonexistent"], "type": AccessType.view.value, "action": "add", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, expect_status=400, ) # If the user doesn't have access to all documents, the operation should not complete. d = DocEntry.find_by_path(self.get_personal_item_path(paths[-1])) for a in d.block.accesses.values(): db.session.delete(a) db.session.commit() self.json_put( f"/permissions/edit", { "groups": ["testuser2", "testuser3"], "type": AccessType.view.value, "action": "add", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, expect_status=403, ) self.json_put( f"/permissions/edit", { "groups": ["testuser2", "testuser3"], "type": AccessType.view.value, "action": "remove", "ids": all_ids, "time": { "type": "always", }, "confirm": False, }, expect_status=403, )
[docs] def test_chaining(self): self.login_test1() d = self.create_doc() # Add an expired right self.test_user_2.grant_access( access_type=AccessType.view, accessible_from=get_current_time(), accessible_to=get_current_time(), block=d, ) db.session.commit() d.document.set_settings( { "auto_confirm": self.get_personal_item_path("nextdoc"), "expire_next_doc_message": "My custom message", } ) self.login_test2() r = self.get(d.url, expect_status=403) self.assertIn("auto_confirm document does not exist", r) self.login_test1() d2 = self.create_doc(path=self.get_personal_item_path("nextdoc")) self.login_test2() r = self.get(d.url, expect_status=403) self.assertIn("Document is not authorized to auto-confirm rights", r) d2.document.set_settings( { "allow_self_confirm_from": d.path, } ) r = self.get(d.url, expect_status=403) self.assertIn( "Cannot get access to target document: No access found for users/test-user-1/nextdoc", r, ) self.assertNotIn("My custom message", r) self.test_user_2.grant_access( access_type=AccessType.view, block=d2, duration=timedelta(hours=2), require_confirm=True, ) db.session.commit() r = self.get(d.url, expect_status=403) self.assertIn("My custom message", r) self.assertIn("Go to the next document", r) # Make sure refreshing the page does not change anything. r = self.get(d.url, expect_status=403) self.assertIn("My custom message", r) self.assertIn("Go to the next document", r) self.get(d2.url, expect_status=403) self.get(d2.url, query_string={"unlock": True}) # test also range confirm self.test_user_2.grant_access( access_type=AccessType.view, block=d2, accessible_to=get_current_time() + timedelta(days=1), require_confirm=True, ) db.session.commit() # make sure can't access at first self.get(d2.url, expect_status=403) # this does the autoconfirm r = self.get(d.url, expect_status=403) self.assertIn("My custom message", r) self.assertIn("Go to the next document", r) # and now there should be access self.get(d2.url)
[docs] def test_chaining_logged_in(self): self.login_test1() d = self.create_doc() self.test_user_2.grant_access( access_type=AccessType.view, accessible_from=get_current_time(), accessible_to=get_current_time(), block=d, ) db.session.commit() d.document.set_settings( { "auto_confirm": self.get_personal_item_path("nextdoc2"), "expire_next_doc_message": "My custom message", } ) d2 = self.create_doc(path=self.get_personal_item_path("nextdoc2")) d2.document.set_settings( { "allow_self_confirm_from": d.path, } ) grant_access( group=UserGroup.get_logged_in_group(), block=d2, access_type=AccessType.view, require_confirm=True, ) db.session.commit() self.login_test2() self.get(d2.url, expect_status=403) self.get(d.url, expect_status=403) self.get(d2.url)
[docs] def test_confirm_with_end_date(self): self.login_test1() d = self.create_doc() self.json_put( f"/permissions/add", { "time": { "from": None, "to": get_current_time() + timedelta(days=1), "type": "range", }, "id": d.id, "type": AccessType.view.value, "groups": ["testuser2"], "confirm": True, }, expect_content={"not_exist": []}, ) self.login_test2() self.get(d.url, expect_status=403) self.login_test1() self.json_put( "/permissions/confirm", { "id": d.id, "type": AccessType.view.value, "group": self.get_test_user_2_group_id(), }, ) self.login_test2() self.get(d.url) self.login_test1() self.json_put( "/permissions/confirm", { "id": d.id, "type": AccessType.view.value, "group": self.get_test_user_2_group_id(), }, expect_status=400, expect_content="view right for testuser2 does not require confirmation or it was already confirmed.", )
[docs] def test_cannot_auto_confirm_too_early(self): self.login_test1() d = self.create_doc() self.test_user_2.grant_access( access_type=AccessType.view, accessible_from=None, accessible_to=get_current_time() + timedelta(days=1), require_confirm=True, block=d, ) db.session.commit() d2 = self.create_doc() d.document.set_settings( { "auto_confirm": d2.path, "expire_next_doc_message": "My custom message", } ) d2.document.set_settings( { "allow_self_confirm_from": d.path, } ) self.login_test2() self.test_user_2.grant_access( access_type=AccessType.view, block=d2, duration=timedelta(hours=2), require_confirm=True, ) db.session.commit() r = self.get(d.url, expect_status=403) self.assertNotIn("My custom message", r) self.assertNotIn("Go to the next document", r) self.test_user_2.grant_access( access_type=AccessType.view, duration=timedelta(days=1), require_confirm=True, block=d, ) db.session.commit() r = self.get(d.url, expect_status=403) self.assertNotIn("My custom message", r) self.assertNotIn("Go to the next document", r) self.test_user_2.grant_access( access_type=AccessType.view, duration=timedelta(days=1), require_confirm=False, block=d, ) db.session.commit() r = self.get(d.url, expect_status=403) self.assertNotIn("My custom message", r) self.assertNotIn("Go to the next document", r)
[docs] def test_auto_confirm_translation(self): self.login_test1() d = self.create_doc() tr = self.create_translation(d) self.test_user_2.grant_access( access_type=AccessType.view, accessible_to=get_current_time() - timedelta(days=1), block=tr, ) db.session.commit() d2 = self.create_doc() d2tr = self.create_translation(d2) trid = d2tr.id d.document.set_settings( { "auto_confirm": d2.path, "expire_next_doc_message": "My custom message", } ) d2.document.set_settings( { "allow_self_confirm_from": d.path, } ) d = DocEntry.find_by_id(d.id) d2 = DocEntry.find_by_id(d2.id) d2.document.clear_mem_cache() synchronize_translations( d, DocumentEditResult(added=d.document.get_paragraphs()) ) synchronize_translations( d2, DocumentEditResult(added=d2.document.get_paragraphs()) ) self.login_test2() d2tr = DocEntry.find_by_id(trid) self.test_user_2.grant_access( access_type=AccessType.view, block=d2tr, accessible_to=get_current_time() + timedelta(days=1), require_confirm=True, ) db.session.commit() self.get(d2tr.url, expect_status=403) self.get(d2tr.url, expect_status=403) r = self.get(tr.url, expect_status=403) self.assertIn("My custom message", r) self.assertIn("Go to the next document", r) self.get(d2tr.url)
[docs] def test_self_expire(self): self.login_test1() d = self.create_doc() self.test_user_2.grant_access( access_type=AccessType.view, accessible_from=get_current_time(), accessible_to=get_current_time(), block=d, ) db.session.commit() self.login_test2() self.json_post("/permissions/selfExpire", {"id": d.id}) self.test_user_2.grant_access( access_type=AccessType.view, accessible_from=get_current_time(), block=d, ) db.session.commit() self.json_post("/permissions/selfExpire", {"id": d.id})
[docs] def test_duration_right(self): self.login_test1() d = self.create_doc() self.json_put( f"/permissions/add", { "time": { "from": None, "to": None, "type": "duration", "duration": "PT4H", }, "id": d.id, "type": AccessType.view.value, "groups": ["testuser2"], "confirm": False, }, )
[docs] def test_mass_permission_remove_partially_redundant(self): """Rights are removed even if the selection contains redundant rows.""" self.login_test1() d = self.create_doc() d2 = self.create_doc() self.test_user_2.grant_access(d, access_type=AccessType.view) db.session.commit() self.json_put( f"/permissions/edit", { "groups": ["testuser2"], "type": AccessType.view.value, "action": "remove", "ids": [d.id, d2.id], "time": { "type": "always", }, "confirm": False, }, ) # The original bug was that db.session.commit() was not getting called when it should have, # so doing a rollback here should reveal the bug in the assert if it reappears. db.session.rollback() d = DocEntry.find_by_id(d.id) self.assertFalse(self.test_user_2.has_view_access(d))
[docs] def test_access_denied_message(self): self.login_test1() d = self.create_doc() self.login_test2() self.get( d.url, expect_status=403, expect_contains="Sorry, you don't have permission to access this resource.", ) d.document.set_settings({"access_denied_message": "You cannot see this."}) self.get(d.url, expect_status=403, expect_contains="You cannot see this.")
[docs] def test_inherit_rights_from_folder(self): self.login_test1() d = self.create_doc( path=self.get_personal_item_path("x/test"), initial_par="#- {plugin=textfield #t}", ) uf = self.upload_file(d, b"test", "test.txt") tr = self.create_translation(d) f = d.parent self.test_user_2.grant_access(f, AccessType.view) db.session.commit() self.login_test2() self.get(d.url, expect_status=403) self.get(tr.url, expect_status=403) self.get(f'/files/{uf["file"]}', expect_status=403) with self.temp_config({"INHERIT_FOLDER_RIGHTS_DOCS": {d.path}}): self.get(d.url) self.get(tr.url) self.get(f'/files/{uf["file"]}') self.mark_as_read(d, d.document.get_paragraphs()[0].get_id()) self.post_answer("textfield", f"{d.id}.t", user_input={"c": "x"})
[docs] def test_no_fulltext_in_manage_with_view(self): self.login_test1() d = self.create_doc( initial_par=""" #- {plugin=csPlugin} -pointsRule: expectCode: "secret answer" """ ) self.test_user_2.grant_access(d, AccessType.view) db.session.commit() self.login_test2() r = self.get(d.get_url_for_view("manage")) self.assertNotIn("secret answer", r)
[docs] def test_permission_clear(self): self.login_test1() d = self.create_doc() self.test_user_2.grant_access(d, AccessType.view) self.test_user_2.grant_access(d, AccessType.edit) db.session.commit() db.session.refresh(d) self.assertTrue(self.test_user_2.has_edit_access(d)) self.assertTrue(self.test_user_2.has_view_access(d)) self.json_put( "/permissions/clear", {"paths": [d.path], "type": AccessType.edit.value} ) d = DocEntry.find_by_id(d.id) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertTrue(self.test_user_2.has_view_access(d)) self.json_put( "/permissions/clear", {"paths": [d.path], "type": AccessType.view.value} ) d = DocEntry.find_by_id(d.id) self.assertFalse(self.test_user_2.has_edit_access(d)) self.assertFalse(self.test_user_2.has_view_access(d)) self.assertTrue(self.test_user_1.has_ownership(d))