from datetime import timedelta
from sqlalchemy import event
from timApp.auth.accesstype import AccessType
from timApp.auth.auth_models import BlockAccess
from timApp.document.docentry import DocEntry
from timApp.folder.folder import Folder
from timApp.item.block import BlockType
from timApp.tests.server.timroutetest import TimRouteTest
from timApp.timdb.sqa import db
from timApp.user.user import User
from timApp.user.userutils import grant_access, get_or_create_default_right_document
from timApp.util.utils import get_current_time
[docs]class FolderTest(TimRouteTest):
[docs] def test_folder_manage(self):
self.login_test3()
f = self.create_folder(self.get_personal_item_path("test_manage"))
self.get(f'/manage/{f["path"]}')
self.login_test2()
self.get(f'/manage/{f["path"]}', expect_status=403)
self.test_user_2.grant_access(Folder.get_by_id(f["id"]), AccessType.manage)
db.session.commit()
self.get(f'/manage/{f["path"]}')
[docs] def test_folder_delete(self):
self.login_test1()
to_delete = self.get_personal_item_path("delete/this")
f = self.create_folder(to_delete)
f2 = Folder.find_by_path(self.get_personal_item_path("delete"))
self.delete(f"/folders/{f2.id}")
self.get(f"/folders/{f2.id}", expect_status=404)
User.get_anon().grant_access(Folder.get_by_id(f["id"]), AccessType.view)
db.session.commit()
self.delete(f'/folders/{f["id"]}', expect_content=self.ok_resp)
doc_path = self.get_personal_item_path("delete/somedoc")
self.create_doc(doc_path)
self.delete(
f"/folders/{f2.id}",
expect_status=400,
expect_content="Folder is already deleted.",
)
self.get(f"/folders/{f2.id}", expect_status=404)
d2 = DocEntry.find_by_path(doc_path)
d2.name = "asd"
db.session.commit()
f2 = Folder.find_by_path(self.get_personal_item_path("delete"))
self.delete(f"/folders/{f2.id}", expect_content=self.ok_resp)
[docs] def test_folders(self):
self.login_test1()
user_folder = self.current_user.get_personal_folder().path
fname = self.get_personal_item_path("testing")
f = self.create_folder(fname)
self.create_folder(
fname,
expect_content={"error": "Item with a same name already exists."},
expect_status=403,
)
new_name = fname + "1"
f2 = self.json_put(f'/rename/{f["id"]}', {"new_name": new_name})
self.assertEqual(new_name, f2["new_name"])
self.json_put(
f'/rename/{f["id"]}',
{"new_name": new_name + "/testing1"},
expect_status=403,
expect_content={"error": "A folder cannot contain itself."},
),
# Create another folder and give access to anonymous users
fname2 = self.get_personal_item_path("testing2")
f3 = self.create_folder(fname2)
User.get_anon().grant_access(Folder.get_by_id(f3["id"]), AccessType.view)
db.session.commit()
t1g = self.get_test_user_1_group_id()
self.get(
"/getItems",
query_string={"folder": user_folder},
expect_content=[
{
"name": "testing1",
"title": "foldertitle",
"id": f["id"],
"isFolder": True,
"modified": "just now",
"path": new_name,
"location": user_folder,
"owners": [{"id": t1g, "name": "testuser1"}],
"rights": {
"browse_own_answers": True,
"can_comment": True,
"can_mark_as_read": True,
"copy": True,
"editable": True,
"manage": True,
"owner": True,
"see_answers": True,
"teacher": True,
},
"unpublished": True,
"public": True,
},
{
"name": "testing2",
"title": "foldertitle",
"id": f3["id"],
"isFolder": True,
"modified": "just now",
"path": fname2,
"location": user_folder,
"owners": [{"id": t1g, "name": "testuser1"}],
"rights": {
"browse_own_answers": True,
"can_comment": True,
"can_mark_as_read": True,
"copy": True,
"editable": True,
"manage": True,
"owner": True,
"see_answers": True,
"teacher": True,
},
"unpublished": False,
"public": True,
},
],
)
self.logout()
self.get("/getItems", query_string={"folder": user_folder}, expect_status=403)
User.get_anon().grant_access(
self.test_user_1.get_personal_folder(), AccessType.view
)
db.session.commit()
self.get(
"/getItems",
query_string={"folder": user_folder},
expect_content=[
{
"name": "testing2",
"title": "foldertitle",
"id": f3["id"],
"isFolder": True,
"modified": "just now",
"path": fname2,
"location": user_folder,
"owners": [{"id": t1g, "name": "testuser1"}],
"rights": {
"browse_own_answers": False,
"can_comment": False,
"can_mark_as_read": False,
"copy": False,
"editable": False,
"manage": False,
"owner": False,
"see_answers": False,
"teacher": False,
},
"unpublished": False,
"public": True,
}
],
)
[docs] def test_folder_view_perf(self):
self.login_test3()
self.create_doc(self.get_personal_item_path("perf/x"))
d = self.create_doc(self.get_personal_item_path("perf/y"))
self.get(d.url)
eng = db.get_engine()
stmts = 0
db.session.expunge_all()
def before_cursor_execute(
conn, cursor, statement: str, parameters, context, executemany
):
stmt = statement.replace("\n", " ")
# print(f"{parameters}\n{stmt}")
nonlocal stmts
stmts += 1
event.listen(eng, "before_cursor_execute", before_cursor_execute)
self.get("/view/" + self.get_personal_item_path("perf"))
event.remove(eng, "before_cursor_execute", before_cursor_execute)
self.assertEqual(stmts, 11)
[docs]class FolderCopyTest(TimRouteTest):
[docs] def test_folder_copy(self):
self.login_test1()
d1 = self.create_doc(self.get_personal_item_path("a/d1"))
d2 = self.create_doc(self.get_personal_item_path("a/d2"))
f1d1 = self.create_doc(self.get_personal_item_path("a/f1/d1"))
_ = self.create_doc(self.get_personal_item_path("a/a-b/x_y"))
_ = self.create_doc(self.get_personal_item_path("a/a-b/x-y"))
_ = self.create_doc(self.get_personal_item_path("a/f1/d2"))
_ = self.create_doc(
self.get_personal_item_path("a/f1/d3"),
initial_par="""
#- {#t1 plugin=csPlugin}
#- {#t1 plugin=csPlugin}""",
)
f2d1 = self.create_doc(self.get_personal_item_path("a/f2/d1"))
f1d1tren = self.create_translation(f1d1, "title_en", "en")
f1d1trsv = self.create_translation(f1d1, "title_sv", "sv")
f1d1tren.document.add_paragraph("hello_en")
f1d1trsv.document.add_paragraph("hello_sv")
a = Folder.find_by_path(self.get_personal_item_path("a"))
a.title = "oldtitle"
f1 = Folder.find_by_path(self.get_personal_item_path("a/f1"))
f2 = Folder.find_by_path(self.get_personal_item_path("a/f2"))
t2g = self.test_user_2.get_personal_group()
grant_access(t2g, f1, AccessType.view)
grant_access(t2g, f2, AccessType.edit)
grant_access(t2g, d2, AccessType.teacher)
db.session.commit()
d1.document.add_paragraph("hello")
f2d1.document.add_paragraph("hi")
self.json_post(
f"/copy/{a.id}",
{"destination": self.get_personal_item_path("a/b"), "exclude": None},
expect_status=403,
expect_content={"error": "Cannot copy folder inside of itself."},
)
self.json_post(
f"/copy/{a.id}",
{"destination": self.get_personal_item_path("b"), "exclude": None},
)
preview = self.json_post(
f"/copy/{a.id}/preview",
{"destination": self.get_personal_item_path("b"), "exclude": ""},
)
self.assertEqual(
{
"dest_exists": True,
"preview": [
{"from": "users/test-user-1/a/d1", "to": "users/test-user-1/b/d1"},
{"from": "users/test-user-1/a/d2", "to": "users/test-user-1/b/d2"},
{"from": "users/test-user-1/a/f1", "to": "users/test-user-1/b/f1"},
{
"from": "users/test-user-1/a/f1/d1",
"to": "users/test-user-1/b/f1/d1",
},
{
"from": "users/test-user-1/a/f1/d2",
"to": "users/test-user-1/b/f1/d2",
},
{
"from": "users/test-user-1/a/f1/d3",
"to": "users/test-user-1/b/f1/d3",
},
{
"from": "users/test-user-1/a/a-b",
"to": "users/test-user-1/b/a-b",
},
{
"from": "users/test-user-1/a/a-b/x_y",
"to": "users/test-user-1/b/a-b/x_y",
},
{
"from": "users/test-user-1/a/a-b/x-y",
"to": "users/test-user-1/b/a-b/x-y",
},
{"from": "users/test-user-1/a/f2", "to": "users/test-user-1/b/f2"},
{
"from": "users/test-user-1/a/f2/d1",
"to": "users/test-user-1/b/f2/d1",
},
],
},
preview,
)
preview = self.json_post(
f"/copy/{a.id}/preview",
{"destination": self.get_personal_item_path("b"), "exclude": "d1"},
)
self.assertEqual(
{
"dest_exists": True,
"preview": [
{"from": "users/test-user-1/a/d2", "to": "users/test-user-1/b/d2"},
{"from": "users/test-user-1/a/f1", "to": "users/test-user-1/b/f1"},
{
"from": "users/test-user-1/a/f1/d2",
"to": "users/test-user-1/b/f1/d2",
},
{
"from": "users/test-user-1/a/f1/d3",
"to": "users/test-user-1/b/f1/d3",
},
{
"from": "users/test-user-1/a/a-b",
"to": "users/test-user-1/b/a-b",
},
{
"from": "users/test-user-1/a/a-b/x_y",
"to": "users/test-user-1/b/a-b/x_y",
},
{
"from": "users/test-user-1/a/a-b/x-y",
"to": "users/test-user-1/b/a-b/x-y",
},
{"from": "users/test-user-1/a/f2", "to": "users/test-user-1/b/f2"},
],
},
preview,
)
b = Folder.find_by_path(self.get_personal_item_path("b"))
self.assertEqual("b", b.title)
d1c = DocEntry.find_by_path(self.get_personal_item_path("b/d1"))
d2c = DocEntry.find_by_path(self.get_personal_item_path("b/d2"))
d2 = DocEntry.find_by_path(self.get_personal_item_path("a/d2"))
f1d1c = DocEntry.find_by_path(self.get_personal_item_path("b/f1/d1"))
f2d1c = DocEntry.find_by_path(self.get_personal_item_path("b/f2/d1"))
f1c = Folder.find_by_path(self.get_personal_item_path("b/f1"))
f2c = Folder.find_by_path(self.get_personal_item_path("b/f2"))
t1g = self.test_user_1.get_personal_group()
self.assertEqual(
[
BlockAccess(
block_id=f1c.id, usergroup_id=t1g.id, type=AccessType.owner.value
),
BlockAccess(
block_id=f1c.id, usergroup_id=t2g.id, type=AccessType.view.value
),
],
list(f1c.block.accesses.values()),
)
self.assertEqual(
[
BlockAccess(
block_id=f2c.id, usergroup_id=t1g.id, type=AccessType.owner.value
),
BlockAccess(
block_id=f2c.id, usergroup_id=t2g.id, type=AccessType.edit.value
),
],
list(f2c.block.accesses.values()),
)
self.assertEqual(
[
BlockAccess(
block_id=d2c.id, usergroup_id=t1g.id, type=AccessType.owner.value
),
BlockAccess(
block_id=d2c.id, usergroup_id=t2g.id, type=AccessType.teacher.value
),
],
list(d2c.block.accesses.values()),
)
self.assertEqual(
[
BlockAccess(
block_id=d2.id, usergroup_id=t1g.id, type=AccessType.owner.value
),
BlockAccess(
block_id=d2.id, usergroup_id=t2g.id, type=AccessType.teacher.value
),
],
list(d2.block.accesses.values()),
)
trs = sorted(f1d1c.translations, key=lambda tr: tr.lang_id)
self.assertEqual(["", "en", "sv"], [tr.lang_id for tr in trs])
self.assertEqual(
["document 1", "title_en", "title_sv"], [tr.title for tr in trs]
)
self.assertEqual(
["hello"], [p.get_markdown() for p in d1c.document.get_paragraphs()]
)
self.assertEqual(
["hi"], [p.get_markdown() for p in f2d1c.document.get_paragraphs()]
)
self.assertEqual(
["hello_en"], [p.get_markdown() for p in trs[1].document.get_paragraphs()]
)
self.assertEqual(
["hello_sv"], [p.get_markdown() for p in trs[2].document.get_paragraphs()]
)
f1d1 = DocEntry.find_by_id(f1d1.id)
self.assertFalse(
{tr.id for tr in f1d1.translations} & {tr.id for tr in f1d1c.translations}
)
self.json_post(
f"/copy/{a.id}",
{"destination": self.get_personal_item_path("b"), "exclude": None},
expect_status=403,
)
[docs] def test_copy_to_existing(self):
self.login_test2()
d1 = self.create_doc(self.get_personal_item_path("a/d1"))
d2 = self.create_doc(self.get_personal_item_path("a/d2"))
f1d1 = self.create_doc(self.get_personal_item_path("a/f1/d1"))
a = Folder.find_by_path(self.get_personal_item_path("a"))
preview = self.json_post(
f"/copy/{a.id}/preview",
{"destination": self.get_personal_item_path("b"), "exclude": ""},
)
self.assertEqual(
{
"dest_exists": False,
"preview": [
{"from": "users/test-user-2/a/d1", "to": "users/test-user-2/b/d1"},
{"from": "users/test-user-2/a/d2", "to": "users/test-user-2/b/d2"},
{"from": "users/test-user-2/a/f1", "to": "users/test-user-2/b/f1"},
{
"from": "users/test-user-2/a/f1/d1",
"to": "users/test-user-2/b/f1/d1",
},
],
},
preview,
)
d1 = self.create_doc(self.get_personal_item_path("b/x1"))
d2 = self.create_doc(self.get_personal_item_path("b/x2"))
f1d1 = self.create_doc(self.get_personal_item_path("b/f1/x1"))
preview = self.json_post(
f"/copy/{a.id}/preview",
{"destination": self.get_personal_item_path("b"), "exclude": ""},
)
self.assertEqual(
{
"dest_exists": True,
"preview": [
{"from": "users/test-user-2/a/d1", "to": "users/test-user-2/b/d1"},
{"from": "users/test-user-2/a/d2", "to": "users/test-user-2/b/d2"},
{"from": "users/test-user-2/a/f1", "to": "users/test-user-2/b/f1"},
{
"from": "users/test-user-2/a/f1/d1",
"to": "users/test-user-2/b/f1/d1",
},
],
},
preview,
)
preview = self.json_post(
f"/copy/{a.id}",
{"destination": self.get_personal_item_path("b"), "exclude": ""},
)
preview = self.json_post(
f"/copy/{a.id}",
{"destination": self.get_personal_item_path("b"), "exclude": ""},
expect_content="Document already exists at path users/test-user-2/b/d1",
expect_status=403,
)
[docs] def test_copy_permission(self):
self.login_test1()
d = self.create_doc(self.get_personal_item_path("perm/a"))
d = self.create_doc(self.get_personal_item_path("perm/b"))
f = d.parent
self.test_user_2.grant_access(f, AccessType.view)
db.session.commit()
self.login_test2()
self.json_post(
f"/copy/{f.id}",
{"destination": self.get_personal_item_path("perm"), "exclude": ""},
expect_content="Missing copy access to folder users/test-user-1/perm",
expect_status=403,
)
self.test_user_2.grant_access(f, AccessType.copy)
db.session.commit()
self.json_post(
f"/copy/{f.id}",
{"destination": self.get_personal_item_path("perm"), "exclude": ""},
expect_content="Missing copy access to document users/test-user-1/perm/a",
expect_status=403,
)
self.login_test1()
d = DocEntry.find_by_path(self.get_personal_item_path("perm/a"))
self.test_user_2.grant_access(d, AccessType.copy)
db.session.commit()
d = DocEntry.find_by_path(self.get_personal_item_path("perm/b"))
self.test_user_2.grant_access(d, AccessType.copy)
db.session.commit()
self.login_test2()
self.json_post(
f"/copy/{f.id}",
{"destination": self.get_personal_item_path("perm"), "exclude": ""},
)
# Make sure the copier is now the owner, and that the copy right has not been copied.
d = DocEntry.find_by_path(self.get_personal_item_path("perm/b"))
self.assertEqual(
{(AccessType.owner, self.test_user_2.get_personal_group())},
{(a.access_type, a.usergroup) for a in d.block.accesses.values()},
)
# Make sure the original rights did not change.
self.login_test1()
d = DocEntry.find_by_path(self.get_personal_item_path("perm/b"))
self.assertEqual(
{
(AccessType.copy, self.test_user_2.get_personal_group()),
(AccessType.owner, self.test_user_1.get_personal_group()),
},
{(a.access_type, a.usergroup) for a in d.block.accesses.values()},
)
[docs] def test_copy_permission_options(self):
self.login_test1()
d = self.create_doc(self.get_personal_item_path("perm_opt/a"))
f = d.parent
self.test_user_2.grant_access(d, AccessType.view)
self.test_user_3.grant_access(
d, AccessType.view, accessible_to=get_current_time() - timedelta(seconds=1)
)
db.session.commit()
self.json_post(
f"/copy/{f.id}",
{
"destination": self.get_personal_item_path("perm_opt_dest1"),
"exclude": "",
"copy_options": {
"copy_active_rights": True,
"copy_expired_rights": False,
},
},
)
d = DocEntry.find_by_path(self.get_personal_item_path("perm_opt_dest1/a"))
self.assertEqual(
{
(AccessType.view, self.test_user_2.get_personal_group()),
(AccessType.owner, self.test_user_1.get_personal_group()),
},
{(a.access_type, a.usergroup) for a in d.block.accesses.values()},
"Only active rights should be copied",
)
self.json_post(
f"/copy/{f.id}",
{
"destination": self.get_personal_item_path("perm_opt_dest2"),
"exclude": "",
"copy_options": {
"copy_active_rights": False,
"copy_expired_rights": True,
},
},
)
d = DocEntry.find_by_path(self.get_personal_item_path("perm_opt_dest2/a"))
self.assertEqual(
{
(AccessType.view, self.test_user_3.get_personal_group()),
(AccessType.owner, self.test_user_1.get_personal_group()),
},
{(a.access_type, a.usergroup) for a in d.block.accesses.values()},
"Only owner and expired right should be copied",
)
self.json_post(
f"/copy/{f.id}",
{
"destination": self.get_personal_item_path("perm_opt_dest3"),
"exclude": "",
"copy_options": {
"copy_active_rights": False,
"copy_expired_rights": False,
},
},
)
d = DocEntry.find_by_path(self.get_personal_item_path("perm_opt_dest3/a"))
self.assertEqual(
{
(AccessType.owner, self.test_user_1.get_personal_group()),
},
{(a.access_type, a.usergroup) for a in d.block.accesses.values()},
"Only owner should have the right to the copied items",
)
[docs] def test_copy_regression(self):
self.login_test1()
_ = self.create_doc(self.get_personal_item_path("x/templates/b"))
f = Folder.find_by_path(self.get_personal_item_path("x"))
self.json_post(
f"/copy/{f.id}",
{"destination": self.get_personal_item_path("c"), "exclude": None},
)
[docs] def test_default_owner_right(self):
"""Exception when an intermediate folder gets a duplicate owner right because of its default rights."""
self.login_test1()
self.create_doc(self.get_personal_item_path("r/a"))
f = Folder.find_by_path(self.get_personal_item_path("r"))
dr = get_or_create_default_right_document(f, BlockType.Folder)
grant_access(self.test_user_1.get_personal_group(), dr, AccessType.owner)
db.session.commit()
# Ensure this won't throw an exception.
self.create_doc(self.get_personal_item_path("r/x/b"))
[docs] def test_no_lose_owners_for_templates(self):
self.login_test1()
f_path = self.get_personal_item_path("test_no_lose_owners")
f = self.create_folder(f_path)
f = Folder.get_by_id(f["id"])
self.test_user_2.grant_access(f, AccessType.owner)
self.test_user_3.grant_access(f, AccessType.owner)
db.session.commit()
f_path = self.get_personal_item_path("test_no_lose_owners/templates")
f = self.create_folder(f_path)
f = Folder.get_by_id(f["id"])
db.session.refresh(f)
self.assertEqual(3, len(f.block.accesses))
[docs]class FolderParentTest(TimRouteTest):
[docs] def test_folder_parents(self):
self.login_test1()
fname = self.get_personal_item_path("x/y/z")
self.create_folder(fname)
f = Folder.find_by_path(fname)
self.assertEqual(
["y", "x", "Test user 1", "users", "All documents"],
[p.title for p in f.parents_to_root()],
)
f = Folder.find_by_path("users")
self.assertEqual(["All documents"], [p.title for p in f.parents_to_root()])
[docs] def test_root_parents(self):
f = Folder.get_root()
self.assertEqual([], [p.title for p in f.parents_to_root()])
[docs]class FolderContentTest(TimRouteTest):
[docs] def test_folder_content(self):
self.login_test1()
d = self.create_doc()
d_id = d.id
docpath = d.path
doctitle = d.title
docname = d.short_name
folder = self.test_user_1.get_personal_folder()
folderpath = folder.path
User.get_anon().grant_access(folder, AccessType.view)
db.session.commit()
self.login_test2()
self.get("/getItems", query_string={"folder": folderpath}, expect_content=[])
User.get_anon().grant_access(d, AccessType.view)
db.session.commit()
self.get(
"/getItems",
query_string={"folder": folderpath},
expect_content=[
{
"id": d_id,
"isFolder": False,
"location": folderpath,
"modified": "just now",
"name": docname,
"owners": [
{
"id": self.get_test_user_1_group_id(),
"name": self.test_user_1.name,
}
],
"path": docpath,
"public": True,
"rights": {
"browse_own_answers": True,
"can_comment": True,
"can_mark_as_read": True,
"copy": False,
"editable": False,
"manage": False,
"owner": False,
"see_answers": False,
"teacher": False,
},
"title": doctitle,
"unpublished": False,
}
],
)