def test_allow_view_if_user_has_perm(self): """ Changing of the document should be restricted only to users who have PERM_WRITE permissions for respective document. """ document_path = os.path.join(BASE_DIR, "data", "berlin.pdf") doc = Document.objects.create_document( user=self.testcase_user, title='berlin.pdf', size=os.path.getsize(document_path), lang='deu', file_name='berlin.pdf', page_count=3) document_url = reverse('core:document', args=(doc.id, )) create_access( node=doc, name=self.margaret_user.username, model_type=Access.MODEL_USER, access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True, } # allow margaret to read/view the document ) # # Margaret does not have access to document # berlin.pdf self.client.login(testcase_user=self.margaret_user) ret = self.client.get(document_url) self.assertEqual(ret.status_code, 200)
def test_newly_created_subfolder_inherits_from_parent(self): """ Newly created folder inherits access permissions from its parent folder. """ new_folder = Folder.objects.create(title="creator_owns_me", user=self.uploader_user) # create custom read access for margaret create_access( node=new_folder, model_type=Access.MODEL_USER, name=self.margaret_user, access_type=Access.ALLOW, access_inherited=False, permissions={READ: True} # allow read access to margaret ) sub_folder = Folder.objects.create(title="subfolder for margaret", user=self.uploader_user, parent=new_folder) # and subfolder inherited access permissions from its parent self.assertTrue(self.uploader_user.has_perms(FULL_ACCESS, sub_folder)) # margaret can read it (because parent folder is readable by parent) self.assertTrue(self.margaret_user.has_perm(READ, sub_folder)) # but margaret cannot write to it, because so are # access permissions inherited from parent folder. self.assertFalse(self.margaret_user.has_perm(WRITE, sub_folder)) self.assertFalse(self.margaret_user.has_perms(FULL_ACCESS, sub_folder))
def test_no_colateral_effect_on_siblings(self): """ User margaret creates two folders: * margaret_privat (f1) * from-margaret-to-elizabet (f2) Margaret decides to grant read access to elizabet on folder (f2). Expected: * elizabet has read access to (f2) * elizabet does *NOT* have read access to (f1) """ # (f1) margaret_privat = Folder.objects.create(title="margaret_privat", user=self.margaret_user) # (f2) for_elizabet = Folder.objects.create( title="from-margaret-to-elizabet", user=self.margaret_user, ) create_access( node=for_elizabet, model_type=Access.MODEL_USER, name=self.elizabet_user, access_type=Access.ALLOW, access_inherited=False, permissions={READ: True} # allow read access to elizabet ) self.assertTrue(self.elizabet_user.has_perm(READ, for_elizabet)) self.assertFalse(self.elizabet_user.has_perm(READ, margaret_privat))
def test_tricky_scenario_1(self): """ There are 4 users: * root (i.e. admin) * margaret * elizabet * uploader margaret and elizabeth are part of group Accountants. Admin user creates a folder titles 'for-Accountants'. Then, he (admin user) assigns access only with READ permision for Accountants group. Expected: * margaret and elizabet have read access on folder 'for-Accountants' * upload does not have read access on folder for-Accountants """ for_accountants = Folder.objects.create( title="for-accountants", user=self.root_user ) accountants = Group.objects.create( name="Accountants" ) accountants.user_set.add(self.margaret_user) accountants.user_set.add(self.elizabet_user) create_access( node=for_accountants, model_type=Access.MODEL_GROUP, name="Accountants", access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True } # allow read access to margaret ) # permission via Accountants Group self.assertTrue( self.elizabet_user.has_perm(READ, for_accountants), "Elizabet must have access to for-accountants folder." " She is part of accountants group!" ) # permission via Accountants Group self.assertTrue( self.margaret_user.has_perm(READ, for_accountants), "Margaret must have access to for-accountants folder." " She is part of accountants group!" ) # uploader is not part of accountants group self.assertFalse( self.uploader_user.has_perm(READ, for_accountants), "Uploader must NOT have access to for-accountants folder." " He is NOT part of accountants group!" )
def test_read_access_only(self): """ User can access documents and folders only for which he/she has read access """ Folder.objects.create( title="Root's Folder", user=self.root_user, ) shared1 = Folder.objects.create( title="Shared 1", user=self.root_user, ) shared2 = Folder.objects.create( title="Shared 2", user=self.root_user, ) Folder.objects.create( title="Margaret's Folder", user=self.margaret_user, ) create_access( node=shared1, model_type=Access.MODEL_USER, name=self.margaret_user, access_type=Access.ALLOW, access_inherited=False, permissions={Access.PERM_READ: True} # allow read access to elizabet ) create_access( node=shared2, model_type=Access.MODEL_USER, name=self.margaret_user, access_type=Access.ALLOW, access_inherited=False, permissions={Access.PERM_READ: True} # allow read access to elizabet ) self.client.login(testcase_user=self.margaret_user) # margaret will see 3 folders: # shared1 (which root user shared with her) # shared2 (which root shared with her) # and her own. json_response, status_code = self._browse_nodes() self.assertEqual(status_code, 200) # will return children Child1, Child2, Child3 self.assertEqual(len(json_response['nodes']), 3) returned_node_titles_set = set( [node['title'] for node in json_response['nodes']]) self.assertEqual(returned_node_titles_set, set(['Shared 1', 'Shared 2', "Margaret's Folder"]))
def node_post_save(sender, node, created, *kwargs): if created: # New node instance was created. # Create associated Access Model: # node creater has full access. create_access( node=node, model_type=Access.MODEL_USER, name=node.user.username, access_type=Access.ALLOW, access_inherited=False, permissions=Access.OWNER_PERMS_MAP # full access ) # Consider this two persons use case: # User uploader uploads scans for user margaret. # Initially document is in uploader's Inbox folder. # Afterwards, uploader moves new document X into common shared_folder. # shared_folder has full access permissions for # boths uploader and margaret. # When margaret sees document X, she copies it into # her private folder X_margaret_fld. X_margaret_fld is # owned only by margaret. # Now document X's path is margaret//X_margaret_fld/X.pdf # If X.pdf access permissions stay same, then uploader will # still have access to X.pdf (via search) which means, # that margaret will need to change manually X.pdf's # access permissions. To avoid manual change of access # permissions from margaret side - papermerge feature # is that X.pdf inherits access permissions from new # parent (X_margaret_fld). Thus, just by copying it, # X.pdf becomes margaret private doc - and uploader # lose its access to it. if node.parent: # current node has a parent? # Following statement covers case when node # is moved from one parent to another parent. # When node moved from one parent to another # it get all its access replaced by access list of the # parent access_diff = AccessDiff( operation=AccessDiff.REPLACE, access_set=node.parent.access_set.all() ) node.propagate_access_changes( access_diffs=[access_diff], apply_to_self=True ) else: # In case node has no parent, all its access permission # remain the same. pass
def test_allow_delete_if_user_has_perm(self): """ Deleting Document should be restricted only to users who have PERM_DELETE permissions """ document_path = os.path.join(BASE_DIR, "data", "berlin.pdf") doc = Document.objects.create_document( user=self.testcase_user, title='berlin.pdf', size=os.path.getsize(document_path), lang='deu', file_name='berlin.pdf', page_count=3) self.assertEqual(Document.objects.count(), 1) nodes_url = reverse('core:document', args=(doc.id, )) nodes_data = [ { 'id': doc.id }, ] create_access( node=doc, name=self.margaret_user.username, model_type=Access.MODEL_USER, access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True, DELETE: True } # allow margaret to delete ) # # Margaret was assigned access to delete the document # berlin.pdf self.client.login(testcase_user=self.margaret_user) ret = self.client.delete( nodes_url, json.dumps(nodes_data), content_type='application/json', HTTP_X_REQUESTED_WITH='XMLHttpRequest', ) self.assertEqual(ret.status_code, 200) # because margaret does not have access to the # document -> it should be still there self.assertEqual(Document.objects.count(), 0)
def test_allow_change_if_user_has_perm(self): """ Changing of the document should be restricted only to users who have PERM_WRITE permissions for respective document. """ document_path = os.path.join(BASE_DIR, "data", "berlin.pdf") doc = Document.objects.create_document( user=self.testcase_user, title='berlin.pdf', size=os.path.getsize(document_path), lang='deu', notes="Margaret, you are allowed to change this.", file_name='berlin.pdf', page_count=3) self.assertEqual(Document.objects.count(), 1) document_url = reverse('core:document', args=(doc.id, )) document_data = {'notes': "It works!"} create_access( node=doc, name=self.margaret_user.username, model_type=Access.MODEL_USER, access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True, WRITE: True } # allow margaret to delete ) # # Margaret does not have access to document # berlin.pdf self.client.login(testcase_user=self.margaret_user) ret = self.client.patch( document_url, json.dumps(document_data), content_type='application/json', HTTP_X_REQUESTED_WITH='XMLHttpRequest', ) self.assertEqual(ret.status_code, 200) # because margaret does not have access to the doc.refresh_from_db() self.assertEqual(doc.notes, "It works!")
def test_create_access_from_another_user_based_access(self): """ Given an access a1 associated to a user margaret. New access a2 is created from a1. Expected: access a2 will have associated user margaret as well. """ new_folder = Folder.objects.create( title="creator_owns_me", user=self.uploader_user ) a1 = create_access( node=new_folder, model_type=Access.MODEL_USER, name="margaret", access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True } # allow read access to margaret ) a2 = Access.create( node=new_folder, access_inherited=True, access=a1 ) self.assertEqual( a2.user.username, "margaret" )
def test_set_access_perms_returns_correct_diffs_2(self): """ 1. New folder is created by uploader 2. New folder has margaret READ & WRITE & DELETE access (allow) 3. User tries to leave for margaret only read permission. (it will set_access_perms only with READ flag) """ node = Folder.objects.create(title="some_folder", user=self.uploader_user) create_access(node=node, model_type=Access.MODEL_USER, name=self.margaret_user, access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True, WRITE: True, DELETE: True }) access_diffs = set_access_perms( node, [{ 'model': 'user', 'name': 'margaret', 'access_type': 'allow', 'permissions': { 'read': True, # only read flag } }]) # only one entry AccessDiff(operation=ADD) self.assertEqual(len(access_diffs), 1, [str(diff) for diff in access_diffs]) self.assertEqual(access_diffs[0].operation, AccessDiff.UPDATE) access = access_diffs[0].pop() self.assertEqual("margaret", access.user.username) self.assertEqual("allow", access.access_type) perms = set() perms.add("read") self.assertEqual(perms, access.perms_codenames())
def test_changes_resursive_inheritance(self): """ 1. uploader creates: F1 -> L1a -> L2a -> L3a F1 -> L1b -> L2b 2. Uploader gives read access to margaret on F1 3. Margaret will have read access on L1a, L1b, L2a, L2b, L3a as well (because of recursive inheritance) """ F1 = Folder.objects.create(title="F1", user=self.uploader_user) L1a = Folder.objects.create(title="L1a", user=self.uploader_user, parent=F1) L2a = Folder.objects.create(title="L2a", user=self.uploader_user, parent=L1a) L3a = Folder.objects.create(title="L3a", user=self.uploader_user, parent=L2a) L1b = Folder.objects.create(title="L1b", user=self.uploader_user, parent=F1) L2b = Folder.objects.create(title="L2b", user=self.uploader_user, parent=L1b) # give margaret read access on topmost folder new_access = create_access( node=F1, model_type=Access.MODEL_USER, name=self.margaret_user, access_type=Access.ALLOW, access_inherited=False, permissions={READ: True} # allow read access to margaret ) # function which does the trick access_diff = AccessDiff(operation=AccessDiff.ADD, access_set=[new_access]) F1.propagate_access_changes(access_diffs=[access_diff], apply_to_self=True) # and assert for folder in [F1, L1a, L1b, L2a, L3a, L2b]: self.assertTrue(self.margaret_user.has_perm(READ, folder), f"margaret: Failed for folder {folder.title}") # uploader still has full access ? self.assertTrue(self.uploader_user.has_perms(FULL_ACCESS, folder), f"uploader: Failed for folder {folder.title}")
def test_user_download_document(self): """ If user has read access to the document (even if he/she is not the owner of the document), then he/she must be able to download it. Scenario: admin user creates a document and assigns read only access for margaret (thus, root is the owner of the document). Expected: Margaret and root user must be able to download the document. Elizabet on the other hand - must not have access to the document (she was not assigned permissions for that) """ document_path = os.path.join( BASE_DIR, "data", "berlin.pdf" ) doc = Document.create_document( user=self.root_user, title='berlin.pdf', size=os.path.getsize(document_path), lang='deu', file_name='berlin.pdf', page_count=3 ) # copy document from its test/data place # to the media storage, as if document was uploaded. default_storage.copy_doc( src=document_path, dst=doc.path.url(), ) create_access( node=doc, name=self.margaret_user.username, model_type=Access.MODEL_USER, access_type=Access.ALLOW, access_inherited=False, permissions={ READ: True } # allow read access to margaret ) self.client.login( testcase_user=self.margaret_user ) url = reverse( 'core:document_download', args=(doc.id,) ) ret = self.client.get(url) self.assertEqual( ret.status_code, 200 ) # also, root/admin must be able to download it self.client.logout() self.client.login( testcase_user=self.root_user ) ret = self.client.get(url) self.assertEqual( ret.status_code, 200 ) self.client.logout() # for elizabet on the other hand, access is forbidden. self.client.login(testcase_user=self.elizabet_user) ret = self.client.get(url) self.assertEqual( ret.status_code, 403 )
def test_move_files_from_uploader_to_margaret(self): """ Uploader creates following folder struture: 1. * upload/for_margaret/mx1 * upload/for_margaret/mx2 2. uploader Creates root level folder shared_documents and assigns it full access to margaret: * upload/for_margaret/mx1 * upload/for_margaret/mx2 * shared_folder <- both uploader and margaret have full access 3. uploader moves folder for_margaret into shared_folder: * upload * shared_folder/for_margaret/mx1 * shared_folder/for_margaret/mx1 4. Margaret can now see and access all for_margaret, mx1 and mx2: * shared_folder/for_margaret/mx1 * shared_folder/for_margaret/mx1 5. Margaret creates private folder margaret_private 6. Margaret moves shared_folder/for_margaret into margaret_private: * shared_folder * margaret_private/for_margaret/mx1 * margaret_private/for_margaret/mx2 7. Now ONLY margaret can see and access * margaret_private/for_margaret/ * margaret_private/for_margaret/mx1 * margaret_private/for_margaret/mx2 Why? Because moved folders for_margaret, mx1, mx2 inherited their new access rights from their new parent while old access rights were deleted. """ # 1 upload = Folder.objects.create( title="upload", user=self.uploader_user ) for_margaret = Folder.objects.create( title="for_margaret", user=self.uploader_user, parent=upload ) mx1 = Folder.objects.create( title="mx1", user=self.uploader_user, parent=for_margaret ) mx2 = Folder.objects.create( title="mx2", user=self.uploader_user, parent=for_margaret ) # 2 shared_folder = Folder.objects.create( title="shared_folder", user=self.uploader_user ) create_access( node=shared_folder, model_type=Access.MODEL_USER, name=self.margaret_user, access_inherited=False, access_type=Access.ALLOW, permissions={ Access.PERM_READ: True, Access.PERM_WRITE: True, Access.PERM_DELETE: True, Access.PERM_TAKE_OWNERSHIP: True, Access.PERM_CHANGE_PERM: True } ) self.assertTrue( self.margaret_user.has_perms(FULL_ACCESS, shared_folder) ) self.assertTrue( self.uploader_user.has_perms(FULL_ACCESS, shared_folder) ) # as of yet, margaret does not has access to mx1, mx2, for_margaret for folder in [mx1, mx2, for_margaret]: self.assertFalse( self.margaret_user.has_perms(FULL_ACCESS, folder), f"Failed for folder {folder.title}" ) # 3 - uploader moves folder for_margaret into shared_folder Folder.objects.move_node(for_margaret, shared_folder) # 4 - margaret now has full access to for_margaret, mx1, mx2 for folder in [mx1, mx2, for_margaret]: # both, margaret and upload have full access self.assertTrue( self.margaret_user.has_perms(FULL_ACCESS, folder), f"Failed for folder {folder.title}" ) self.assertTrue( self.uploader_user.has_perms(FULL_ACCESS, folder), f"Uploader has access to {folder.title}. Wrong code!" ) # 5. Margaret creates private folder margaret_private private_folder = Folder.objects.create( title="private", user=self.margaret_user ) # 6. Margaret moves shared_folder/for_margaret into margaret_private: Folder.objects.move_node( for_margaret, # node to move private_folder # new parent ) # 7 for folder in [mx1, mx2, for_margaret]: self.assertTrue( self.margaret_user.has_perms(FULL_ACCESS, folder), f"Failed for folder {folder.title}" ) # uploader lost its access permissions for mx1, mx2, # for_margaret folders self.assertFalse( self.uploader_user.has_perms(FULL_ACCESS, folder), f"Failed for folder {folder.title}" )