django-restricted-resource-2016.8/0000755000175000017500000000000012752617451016761 5ustar neilneil00000000000000django-restricted-resource-2016.8/ci-run0000755000175000017500000000015212607717005020075 0ustar neilneil00000000000000#!/bin/sh set -x set -e pep8 --ignore E501 . ./django_restricted_resource/test_project/manage.py test django-restricted-resource-2016.8/setup.cfg0000644000175000017500000000017712752617451020607 0ustar neilneil00000000000000[upload_docs] upload-dir = build/sphinx/html [upload] sign = True [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django-restricted-resource-2016.8/.gitreview0000644000175000017500000000012312611402151020742 0ustar neilneil00000000000000[gerrit] host=review.linaro.org port=29418 project=lava/django-restricted-resource django-restricted-resource-2016.8/PKG-INFO0000644000175000017500000000141712752617451020061 0ustar neilneil00000000000000Metadata-Version: 1.1 Name: django-restricted-resource Version: 2016.8 Summary: Base model for Django that adds simple and efficient ownership and access control. Home-page: http://git.linaro.org/git/lava/django-restricted-resource.git Author: Linaro Limited Author-email: lava-team@linaro.org License: LGPLv3 Description: UNKNOWN Keywords: django,ownership,models Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.5 django-restricted-resource-2016.8/COPYING0000644000175000017500000001672712321741146020020 0ustar neilneil00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. django-restricted-resource-2016.8/setup.py0000755000175000017500000000352712752603774020510 0ustar neilneil00000000000000#!/usr/bin/env python # Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . from setuptools import setup setup( name='django-restricted-resource', version="2016.8", author="Linaro Limited", author_email="lava-team@linaro.org", description="Base model for Django that adds simple and efficient ownership and access control.", url='http://git.linaro.org/git/lava/django-restricted-resource.git', test_suite='django_restricted_resource.test_project.tests.run_tests', license='LGPLv3', keywords=['django', 'ownership', 'models'], classifiers=[ "Development Status :: 4 - Beta", 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", ], zip_safe=True, packages=['django_restricted_resource'], # dependencies install_requires=['django >= 1.7'], tests_require=['django-testscenarios >= 0.9'], ) django-restricted-resource-2016.8/MANIFEST.in0000644000175000017500000000006212322744720020506 0ustar neilneil00000000000000include django_restricted_resource/test_project/* django-restricted-resource-2016.8/.gitignore0000644000175000017500000000011212314610663020733 0ustar neilneil00000000000000*.egg *.egg-info *.pyc build coverage_report coverage_report_log.txt dist django-restricted-resource-2016.8/django_restricted_resource.egg-info/0000755000175000017500000000000012752617451026054 5ustar neilneil00000000000000django-restricted-resource-2016.8/django_restricted_resource.egg-info/PKG-INFO0000644000175000017500000000141712752617451027154 0ustar neilneil00000000000000Metadata-Version: 1.1 Name: django-restricted-resource Version: 2016.8 Summary: Base model for Django that adds simple and efficient ownership and access control. Home-page: http://git.linaro.org/git/lava/django-restricted-resource.git Author: Linaro Limited Author-email: lava-team@linaro.org License: LGPLv3 Description: UNKNOWN Keywords: django,ownership,models Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.5 django-restricted-resource-2016.8/django_restricted_resource.egg-info/SOURCES.txt0000644000175000017500000000174112752617451027743 0ustar neilneil00000000000000.gitignore .gitreview COPYING MANIFEST.in ci-run setup.cfg setup.py django_restricted_resource/__init__.py django_restricted_resource/managers.py django_restricted_resource/models.py django_restricted_resource/test_utils.py django_restricted_resource/tests.py django_restricted_resource/utils.py django_restricted_resource.egg-info/PKG-INFO django_restricted_resource.egg-info/SOURCES.txt django_restricted_resource.egg-info/dependency_links.txt django_restricted_resource.egg-info/pbr.json django_restricted_resource.egg-info/requires.txt django_restricted_resource.egg-info/top_level.txt django_restricted_resource.egg-info/zip-safe django_restricted_resource/test_project/__init__.py django_restricted_resource/test_project/__init__.pyc django_restricted_resource/test_project/manage.py django_restricted_resource/test_project/settings.py django_restricted_resource/test_project/settings.pyc django_restricted_resource/test_project/tests.py django_restricted_resource/test_project/tests.pycdjango-restricted-resource-2016.8/django_restricted_resource.egg-info/top_level.txt0000644000175000017500000000003312752617451030602 0ustar neilneil00000000000000django_restricted_resource django-restricted-resource-2016.8/django_restricted_resource.egg-info/dependency_links.txt0000644000175000017500000000000112752617451032122 0ustar neilneil00000000000000 django-restricted-resource-2016.8/django_restricted_resource.egg-info/zip-safe0000644000175000017500000000000112314612257027475 0ustar neilneil00000000000000 django-restricted-resource-2016.8/django_restricted_resource.egg-info/requires.txt0000644000175000017500000000001612752617451030451 0ustar neilneil00000000000000django >= 1.7 django-restricted-resource-2016.8/django_restricted_resource.egg-info/pbr.json0000644000175000017500000000005712607706002027522 0ustar neilneil00000000000000{"is_release": false, "git_version": "c76130b"}django-restricted-resource-2016.8/django_restricted_resource/0000755000175000017500000000000012752617451024362 5ustar neilneil00000000000000django-restricted-resource-2016.8/django_restricted_resource/test_utils.py0000644000175000017500000001453312752603672027141 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . import inspect import itertools from django.contrib.auth.models import (AnonymousUser, User, Group) from django.db import models from django_testscenarios.ubertest import (TestCase, TestCaseWithScenarios) from django_restricted_resource.models import RestrictedResource class TestCaseWithInvariants(TestCaseWithScenarios): """ TestCase that generates test scenarios based on the possible combination of invariants. Invariants are declared by the 'invariants' variable defined in class scope. The variable must point to a dictionary. Each element of that dictionary becomes a test invariant. The value of that dictionary may be one of: * list: Each of the elements is checked. If all elements are of simple type (int, bool, string) then parameter description will contain them as-is, otherwise they will be described as "variant-N" * dict: Each of the values is checked. In addition the keys will be used to construct meaningful parameter descriptions. """ def _get_invariants(self): return getattr(self, 'invariants', {}) def _get_scenarios(self): if hasattr(self, "scenarios"): return self.scenarios self.scenarios = self._get_all_possible_scenarios( self._get_invariants()) return self.scenarios def _dict_to_keys_and_values(self, d): items = list(d.items()) keys = [first for (first, second) in items] values = [second for (first, second) in items] return keys, values def _get_all_possible_scenarios(self, invariants): scenarios = [] invariant_keys, invariant_values = self._dict_to_keys_and_values(invariants) scenario_ids_list = [] scenario_params_list = [] for value in invariant_values: if isinstance(value, list): if all([isinstance(variant, (int, bool, str)) for variant in value]): scenario_ids_list.append([repr(variant) for variant in value]) else: scenario_ids_list.append(['variant-%d' % variant for variant in range(len(value))]) scenario_params_list.append(value) elif isinstance(value, dict): k, v = self._dict_to_keys_and_values(value) scenario_ids_list.append(k) scenario_params_list.append(v) for scenario_ids, scenario_params in zip( itertools.product(*scenario_ids_list), itertools.product(*scenario_params_list)): parameters = dict(zip(invariant_keys, scenario_params)) name = ", ".join([ "%s=%s" % (invariant_key, param_id) for (invariant_key, param_id) in zip(invariant_keys, scenario_ids)]) scenario = (name, parameters) scenarios.append(scenario) return scenarios def setUp(self): super(TestCaseWithScenarios, self).setUp() # Evaluate lazy invariants now that `self' is around for invariant in self._get_invariants().keys(): value = getattr(self, invariant) if inspect.isfunction(value): value = value(self) # Some keys are lambda pointing to another key (which is # sometime also a lambda). Depending on the order of the keys # returned by .keys() the pointed value might still be a # lambda. # For instance with: # invariants = {'owner': lambda self: ..., # 'accessible_by_principal': lambda self: self.owner } # when 'accessible_by_principal' is evaluated before 'owner', # it should be evaluated twice. if inspect.isfunction(value): value = value(self) setattr(self, invariant, value) class ExampleRestrictedResource(RestrictedResource): """ Dummy model to get non-abstract model that inherits from RestrictedResource """ name = models.CharField(max_length=100, null=True, unique=True) class Meta: ordering = ['name'] class FixtureHelper(object): def getUniqueString(self, prefix=None, max_length=None): value = super(FixtureHelper, self).getUniqueString(prefix) if max_length is not None: if len(value) >= max_length: value = super(FixtureHelper, self).getUniqueString("short") if len(value) >= max_length: raise ValueError("Unable to satisfy request for random string with max_length=%d" % max_length) return value def getUniqueStringForField(self, model, field_name): return self.getUniqueString(max_length=model._meta.get_field(field_name).max_length) def getUniqueUser(self, is_active=True): user = User.objects.create( username=self.getUniqueStringForField(User, "username"), is_active=is_active) self.addCleanup(user.delete) return user def getUniqueGroup(self): group = Group.objects.create( name=self.getUniqueStringForField(Group, "name")) self.addCleanup(group.delete) return group def getUniqueResource(self, owner, is_public, name=None): resource = ExampleRestrictedResource.objects.create( owner=owner, is_public=is_public, name=name) self.addCleanup(resource.delete) return resource def add_resources(self, resources, owner, is_public): for name in resources: self.getUniqueResource( name=name, owner=owner, is_public=is_public) django-restricted-resource-2016.8/django_restricted_resource/test_project/0000755000175000017500000000000012752617451027067 5ustar neilneil00000000000000django-restricted-resource-2016.8/django_restricted_resource/test_project/manage.py0000755000175000017500000000247712321741146030675 0ustar neilneil00000000000000#!/usr/bin/env python # Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . import os import sys def find_sources(): base_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "../..") if os.path.exists(os.path.join(base_path, "django_restricted_resource")): sys.path.insert(0, base_path) if __name__ == "__main__": find_sources() settings_module = "django_restricted_resource.test_project.settings" os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module) from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-restricted-resource-2016.8/django_restricted_resource/test_project/settings.pyc0000644000175000017500000000102712323040147031426 0ustar neilneil00000000000000ó fÂGSc @s?ddlmZeƒjedddddddgƒƒd S( iÿÿÿÿ(t gen_settingst SECRET_KEYtneeded_but_not_securedtINSTALLED_APPSsdjango.contrib.authsdjango.contrib.contenttypessdjango.contrib.sessionstdjango_restricted_resourceN(tdjango_testproject.settingsRtlocalstupdate(((sq/home/neil/code/lava/packaging/git/django-restricted-resource/django_restricted_resource/test_project/settings.pyts django-restricted-resource-2016.8/django_restricted_resource/test_project/__init__.pyc0000644000175000017500000000031012323040147031317 0ustar neilneil00000000000000ó fÂGSc@sdS(N((((sq/home/neil/code/lava/packaging/git/django-restricted-resource/django_restricted_resource/test_project/__init__.pytsdjango-restricted-resource-2016.8/django_restricted_resource/test_project/__init__.py0000644000175000017500000000140712321741146031171 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . django-restricted-resource-2016.8/django_restricted_resource/test_project/tests.pyc0000644000175000017500000000112712323040147030731 0ustar neilneil00000000000000ó fÂGSc@s3ddlmZd„Zedkr/eƒndS(iÿÿÿÿ(t run_tests_forcCs tdƒS(Ns0django_restricted_resource.test_project.settings(R(((sn/home/neil/code/lava/packaging/git/django-restricted-resource/django_restricted_resource/test_project/tests.pyt run_testsst__main__N(tdjango_testproject.testsRRt__name__(((sn/home/neil/code/lava/packaging/git/django-restricted-resource/django_restricted_resource/test_project/tests.pyts  django-restricted-resource-2016.8/django_restricted_resource/test_project/settings.py0000644000175000017500000000213112321741146031265 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . from django_testproject.settings import gen_settings locals().update( gen_settings( SECRET_KEY='needed_but_not_secured', INSTALLED_APPS=[ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django_restricted_resource', ] ) ) django-restricted-resource-2016.8/django_restricted_resource/test_project/tests.py0000644000175000017500000000171712321741146030600 0ustar neilneil00000000000000# Copyright (C) 2010, 2011 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . from django_testproject.tests import run_tests_for def run_tests(): return run_tests_for("django_restricted_resource.test_project.settings") if __name__ == '__main__': run_tests() django-restricted-resource-2016.8/django_restricted_resource/utils.py0000644000175000017500000000216512321741146026067 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . def filter_bogus_users(user): """ Check for bogus instances of user (anonymous, disabled, etc) and change them to None. This simplifies user comparison and handling. If the user is `None' then he's not trusted, period, no need to check deeper. """ if user is not None and user.is_authenticated() and user.is_active: return user django-restricted-resource-2016.8/django_restricted_resource/__init__.py0000644000175000017500000000145212321741146026464 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . __version__ = (0, 2, 8, "dev", 0) django-restricted-resource-2016.8/django_restricted_resource/models.py0000644000175000017500000001630212752603672026221 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . """ Unit tests for django-restricted-resources """ from django.core.exceptions import ValidationError from django.contrib.auth.models import (AnonymousUser, User, Group) from django.db import models from django_restricted_resource.utils import filter_bogus_users from django_restricted_resource.managers import RestrictedResourceManager class RestrictedResource(models.Model): """ Model for handling resource ownership and access rights. This is not a generic permission framework. Instead it's a rigid and simple but arguably useful model with the following properties: * resources are owned by exactly one user or exactly one group * resources are accessible to anyone if marked as public * resources are accessible to owner if owned by user * resources are accessible to members if owned by group * What the access actually permits is not defined This similar to the UNIX permission system with the following differences: * There are no separate READ, WRITE, EXECUTE nor STICKY bits per any of the possible user classes (OWNER, GROUP MEMBER, OTHER). Instead the application is free to use any desired policy to define actions a particular user may invoke on a resource that he can already access. * There is a dedicated "public" flag indicating that anyone can access the specified resource. * (important) Enumeration of resources that user can access is very efficient when implemented in a relational database. """ NO_ACCESS, PUBLIC_ACCESS, PRIVATE_ACCESS, SHARED_ACCESS = range(4) user = models.ForeignKey(User, null=True, blank=True) group = models.ForeignKey(Group, null=True, blank=True) is_public = models.BooleanField(default=False) objects = RestrictedResourceManager() class Meta: abstract = True def save(self, *args, **kwargs): """ Validate and save the resource """ self.clean() return super(RestrictedResource, self).save(*args, **kwargs) def clean(self): """ Make sure that ownership constraint is met. RestrictedResource must be owned by exactly one user or exactly one group. """ if self.user is not None and self.group is not None: raise ValidationError( 'Cannot be owned by user and group at the same time') if self.user is None and self.group is None: raise ValidationError( 'Must be owned by someone') if hasattr(models.Model, "clean"): return super(RestrictedResource, self).clean() def _set_owner(self, owner): if not isinstance(owner, (User, Group)): raise TypeError("Expected User or Group instance as argument") if isinstance(owner, User): self.user = owner self.group = None else: self.group = owner self.user = None def _get_owner(self): return self.user or self.group owner = property( _get_owner, _set_owner, None, "Principal owner (either User or Group) of this restricted resource") def is_accessible_by(self, principal): """ Check if the resource can be accessed by the specified principal. """ return self.get_access_type(principal) != self.NO_ACCESS def is_owned_by(self, principal): """ Check if the resource is owned by the specified principal. The principal may be any user or group """ if principal is None: return False # If the principal is an User then this object is owned by that user or # the group the user belongs to. if isinstance(principal, (User, AnonymousUser, type(None))): user = filter_bogus_users(principal) if user is None: return False if self.user is not None: return self.user == user else: return self.group in user.groups.all() # If the principal is a Group then this object is owned by that group elif isinstance(principal, Group): group = principal return self.group == group else: raise TypeError("Expected User or Group instance as argument") def get_access_type(self, principal): """ Determine the mode of access the principal has to this object: Possible access types: NO_ACCESS No access to restricted resource PUBLIC_ACCESS Granted to: * User (including owner) accessing publicly available resource * Groups accessing publicly available resource * None (including Anonymous users and blocked users) accessing publicly available resources PRIVATE_ACCESS Granted to: * Owner accessing otherwise private resource SHARED_ACCESS Granted to: * User accessing restricted data via membership * Groups accessing restricted data they own """ if isinstance(principal, (User, AnonymousUser, type(None))): user = filter_bogus_users(principal) return self._get_access_type_for_user(user) elif isinstance(principal, Group): group = principal return self._get_access_type_for_group(group) else: raise TypeError("Expected User or Group instance as argument") def _get_access_type_for_user(self, user): # Allow anyone to access public data if self.is_public: return self.PUBLIC_ACCESS # Allow access for owners if self.user is not None and self.user == user: return self.PRIVATE_ACCESS # Allow access for team members if self.group is not None and user is not None: if self.group in user.groups.all(): return self.SHARED_ACCESS # Finally, disallow return self.NO_ACCESS def _get_access_type_for_group(self, group): # Allow anyone to access public data if self.is_public: return self.PUBLIC_ACCESS # Allow access to group if group is the owner if self.group is not None and self.group == group: return self.SHARED_ACCESS # Finally, disallow return self.NO_ACCESS django-restricted-resource-2016.8/django_restricted_resource/managers.py0000644000175000017500000001251212653677105026534 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . """ Module with model manager for RestrictedResource """ import django from django.contrib.auth.models import (User, AnonymousUser, Group) from django.db.models import Q from django.db import models from django_restricted_resource.utils import filter_bogus_users if django.VERSION < (1, 7): QuerySet = models.query.QuerySet else: QuerySet = models.QuerySet class RestrictedResourceQuerySet(QuerySet): """ Model manager for RestrictedResource and subclasses that has additional methods. The extra methods are: * owned_by_principal(principal) * accessible_by_principal(principal) Both methods allow for efficient enumeration of owned and accessible resources (respectively). There is one extra convenience method: * accessible_by_anyone() That is equivalent to accessible_by_principal(None) but is more expressive in intent. """ def owned_by_principal(self, principal): """ Return a QuestySet of RestrictedResource instances that are owned by the specified principal, which can be a User or Group instance. """ if isinstance(principal, (User, AnonymousUser, type(None))): user = filter_bogus_users(principal) return self._owned_by_user(user) elif isinstance(principal, Group): group = principal return self._owned_by_group(group) else: raise TypeError("Expected User or Group instance as argument") def accessible_by_principal(self, principal): """ Return a QuerySet of RestrictedResource instances that can be accessed by specified principal. The principal may be None, AnonymousUser, valid User or valid Group. Note: All objects that can be accessed are returned, not just objects with exclusive access. To determine why a particular principal can access a particular resource use RestrictedResource.get_access_type(principal). """ if isinstance(principal, (User, AnonymousUser, type(None))): user = filter_bogus_users(principal) return self._accessible_by_user(user) elif isinstance(principal, Group): group = principal return self._accessible_by_group(group) else: raise TypeError("Expected User or Group instance as argument") def accessible_by_anyone(self): """ Return a QuerySet of BundleStream instances that can be accessed by anyone. """ return self._accessible_by_user(None) def _accessible_by_user(self, user): if user is None: return self.filter(is_public=True) else: return self.filter( Q(is_public=True) | Q(user=user) | Q(group__in=user.groups.all())) def _accessible_by_group(self, group): # None gets mapped to users, there is no chance to get None here assert group is not None return self.filter( Q(is_public=True) | Q(group=group)) def _owned_by_user(self, user): if user is None: return self.none() else: return self.filter( Q(user=user) | Q(group__in=user.groups.all())) def _owned_by_group(self, group): # None gets mapped to users, there is no chance to get None here assert group is not None return self.filter(group=group) if django.VERSION >= (1, 7): RestrictedResourceManager = models.Manager.from_queryset( RestrictedResourceQuerySet) else: # TODO: Remove once the support for django 1.6 has been dropped. class RestrictedResourceManager(models.Manager): def get_query_set(self): return RestrictedResourceQuerySet(self.model, using=self._db) def owned_by_principal(self, principal): return self.get_query_set().owned_by_principal(principal) def accessible_by_principal(self, principal): return self.get_query_set().accessible_by_principal(principal) def accessible_by_anyone(self): return self.get_query_set().accessible_by_anyone() def _accessible_by_user(self, user): return self.get_query_set()._accessible_by_user(user) def _accessible_by_group(self, group): return self.get_query_set()._accessible_by_group(group) def _owned_by_user(self, user): return self.get_query_set()._owned_by_user(user) def _owned_by_group(self, group): return self.get_query_set()._owned_by_group(group) django-restricted-resource-2016.8/django_restricted_resource/tests.py0000644000175000017500000004303212752603611026071 0ustar neilneil00000000000000# Copyright (C) 2010 Linaro Limited # # Author: Zygmunt Krynicki # # This file is part of django-restricted-resource. # # django-restricted-resource is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # django-restricted-resource is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with django-restricted-resource. If not, see . """ Unit tests for django_restricted_resource application """ from django.contrib.auth.models import (AnonymousUser, User, Group) from django.core.exceptions import ValidationError from django_testscenarios.ubertest import TestCaseWithScenarios from django_restricted_resource.test_utils import ( ExampleRestrictedResource, FixtureHelper, TestCase, TestCaseWithInvariants, TestCaseWithScenarios, ) from django_restricted_resource.models import RestrictedResource import django if hasattr(django, 'setup'): django.setup() class ResourceCleanTests(FixtureHelper, TestCase): def __hash__(self): return hash(repr(self)) def test_clean_raises_exception_when_owner_is_not_set(self): resource = RestrictedResource() self.assertRaises(ValidationError, resource.clean) def test_clean_raises_exception_when_both_user_and_group_is_set(self): user = self.getUniqueUser() group = self.getUniqueGroup() resource = RestrictedResource(user=user, group=group) self.assertRaises(ValidationError, resource.clean) def test_clean_is_okay_when_just_user_set(self): user = self.getUniqueUser() resource = RestrictedResource(user=user) self.assertEqual(resource.clean(), None) def test_clean_is_okay_when_just_group_set(self): group = self.getUniqueGroup() resource = RestrictedResource(group=group) self.assertEqual(resource.clean(), None) class ResourceOwnerTest(FixtureHelper, TestCase): """ Tests for the owner property """ def __hash__(self): return hash(repr(self)) def test_user_is_owner(self): user = self.getUniqueUser() resource = ExampleRestrictedResource(user=user) self.assertEqual(resource.owner, user) def test_group_is_owner(self): group = self.getUniqueGroup() resource = ExampleRestrictedResource(group=group) self.assertEqual(resource.owner, group) def test_owner_can_be_changed_to_group(self): group = self.getUniqueGroup() resource = ExampleRestrictedResource() resource.owner = group resource.save() self.assertEqual(resource.group, group) self.assertEqual(resource.user, None) def test_owner_can_be_changed_to_user(self): user = self.getUniqueUser() resource = ExampleRestrictedResource() resource.owner = user resource.save() self.assertEqual(resource.group, None) self.assertEqual(resource.user, user) def test_owner_cannot_be_none(self): resource = ExampleRestrictedResource() self.assertRaises(TypeError, setattr, resource, "owner", None) class PrincipalTypeIsChecked( FixtureHelper, TestCaseWithInvariants): invariants = dict( principal=[1, object(), {}, [], "string"], ) def __hash__(self): return hash(repr(self)) def test_owner_assignment(self): resource = ExampleRestrictedResource() self.assertRaises(TypeError, setattr, resource, "owner", self.principal) def test_is_accessible_by(self): resource = ExampleRestrictedResource() self.assertRaises(TypeError, resource.is_accessible_by, self.principal) def test_is_owned_by(self): resource = ExampleRestrictedResource() self.assertRaises(TypeError, resource.is_owned_by, self.principal) def test_manger_owned_by_set(self): self.assertRaises(TypeError, ExampleRestrictedResource.objects.owned_by_principal, self.principal) def test_manger_accessible_by_set(self): self.assertRaises(TypeError, ExampleRestrictedResource.objects.accessible_by_principal, self.principal) class OthersDoNotOwnResource( FixtureHelper, TestCaseWithInvariants): """ RestrictedResource.is_owned_by() returns False for everyone but the owner """ invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), accessing_principal=dict( nothing=None, anonymous_user=AnonymousUser(), inactive_user=lambda self: self.getUniqueUser(is_active=False), unrelated_user=lambda self: self.getUniqueUser(), unrelated_group=lambda self: self.getUniqueGroup(), ), is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def test(self): resource = self.getUniqueResource( owner=self.owner, is_public=self.is_public) self.assertFalse( resource.is_owned_by(self.accessing_principal)) class OwnerOwnsResource( FixtureHelper, TestCaseWithInvariants): """ RestrictedResource.is_owned_by() returns True for the owner """ invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def test(self): resource = self.getUniqueResource( owner=self.owner, is_public=self.is_public) self.assertTrue(resource.is_owned_by(self.owner)) class GroupMemberOwnsResource(FixtureHelper): def test(self): """ RestrictedResource.is_owned_by() returns True for owning group members """ group = self.getUniqueGroup() user = self.getUniqueUser() user.groups.add(group) resource = self.getUniqueResource(owner=group) self.assertTrue(resource.is_owned_by(user)) class ResourceManagerOwnerSetFindsNoMatchesForOthers( FixtureHelper, TestCaseWithInvariants): """ RestrictedResourceManager.owned_by_principal() does not return anything for non-owners """ invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), accessing_principal=dict( nothing=None, anonymous_user=AnonymousUser(), inactive_user=lambda self: self.getUniqueUser(is_active=False), unrelated_user=lambda self: self.getUniqueUser(), unrelated_group=lambda self: self.getUniqueGroup(), ), is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def test(self): manager = ExampleRestrictedResource.objects resource = self.getUniqueResource( owner=self.owner, is_public=self.is_public) result = manager.owned_by_principal(self.accessing_principal) self.assertEqual(result.count(), 0) class ResourceManagerOwnerSetFindsMatchesForOwner( FixtureHelper, TestCaseWithInvariants): invariants = dict( num_objects=[0, 10, 500], owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def test(self): for i in range(self.num_objects): self.getUniqueResource( owner=self.owner, name=str(i), is_public=self.is_public) manager = ExampleRestrictedResource.objects result = manager.owned_by_principal(self.owner) self.assertEqual(result.count(), self.num_objects) class ResourceManagerOwnerSetFindsMatchesForOwnerGroupMember( FixtureHelper, TestCaseWithInvariants): invariants = dict( num_objects=[0, 10, 500], is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def test(self): group = self.getUniqueGroup() user = self.getUniqueUser() user.groups.add(group) for i in range(self.num_objects): self.getUniqueResource( owner=group, name=str(i), is_public=self.is_public) manager = ExampleRestrictedResource.objects result = manager.owned_by_principal(user) self.assertEqual(result.count(), self.num_objects) class EveryoneHasPublicAccessToPublicResources( FixtureHelper, TestCaseWithInvariants): """ Tests for the get_access_type() method """ invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), accessing_principal=dict( nothing=None, anonymous_user=AnonymousUser(), inactive_user=lambda self: self.getUniqueUser(is_active=False), unrelated_user=lambda self: self.getUniqueUser(), unrelated_group=lambda self: self.getUniqueGroup(), owner=lambda self: self.owner, ), is_public=[True, False], ) def __hash__(self): return hash(repr(self)) def setUp(self): super(EveryoneHasPublicAccessToPublicResources, self).setUp() self.resource = self.getUniqueResource( owner=self.owner, is_public=True) def test_get_access_type(self): self.assertEqual( self.resource.get_access_type(self.accessing_principal), self.resource.PUBLIC_ACCESS) def test_is_accessible_by(self): self.assertTrue( self.resource.is_accessible_by(self.accessing_principal)) class NobodyButTheOwnerHasAccessToNonPublicUserResources( FixtureHelper, TestCaseWithInvariants): """ Tests for the get_access_type() method """ def __hash__(self): return hash(repr(self)) invariants = dict( accessing_principal=dict( nothing=None, anonymous_user=AnonymousUser(), inactive_user=lambda self: self.getUniqueUser(is_active=False), unrelated_user=lambda self: self.getUniqueUser(), unrelated_group=lambda self: self.getUniqueGroup(), ) ) def test_everyone_else(self): owner = self.getUniqueUser() resource = self.getUniqueResource(is_public=False, owner=owner) self.assertEqual( resource.get_access_type(self.accessing_principal), resource.NO_ACCESS) class OwnerHasPrivateAccessToNonPublicUserResources( FixtureHelper, TestCase): """ Tests for the get_access_type() method """ def __hash__(self): return hash(repr(self)) def test_owner(self): owner = self.getUniqueUser() resource = self.getUniqueResource(is_public=False, owner=owner) self.assertEqual( resource.get_access_type(owner), resource.PRIVATE_ACCESS) class GroupMembersGetSharedAccessToNonPublicGroupResources( FixtureHelper, TestCase): def __hash__(self): return hash(repr(self)) def test_get_access_type_for_owning_group(self): owner = self.getUniqueGroup() resource = self.getUniqueResource(owner=owner, is_public=False) self.assertEqual( resource.get_access_type(owner), resource.SHARED_ACCESS) def test_get_access_type_for_related_user(self): owner = self.getUniqueGroup() resource = self.getUniqueResource(owner=owner, is_public=False) related_user = self.getUniqueUser() related_user.groups.add(owner) self.assertEqual( resource.get_access_type(related_user), resource.SHARED_ACCESS) class ResourceManagerAccessibleSetFindsOnlyPublicElementsForNonOwners( FixtureHelper, TestCaseWithInvariants): invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), accessing_principal=dict( nothing=None, anonymous_user=AnonymousUser(), inactive_user=lambda self: self.getUniqueUser(is_active=False), unrelated_user=lambda self: self.getUniqueUser(), unrelated_group=lambda self: self.getUniqueGroup(), ) ) def __hash__(self): return hash(repr(self)) def test(self): manager = ExampleRestrictedResource.objects self.add_resources(["a", "b", "c"], owner=self.owner, is_public=True) self.add_resources(["x", "y", "z"], owner=self.owner, is_public=False) resources = manager.accessible_by_principal(self.accessing_principal) self.assertEqual( [res.name for res in resources], ["a", "b", "c"]) class ResourceManagerAccessibleByAnyoneSetFindsOnlyPublicElements( FixtureHelper, TestCaseWithInvariants): invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), ) def __hash__(self): return hash(repr(self)) def test(self): self.add_resources(["a", "b", "c"], owner=self.owner, is_public=True) self.add_resources(["x", "y", "z"], owner=self.owner, is_public=False) manager = ExampleRestrictedResource.objects resources = manager.accessible_by_anyone() self.assertEqual( [res.name for res in resources], ["a", "b", "c"]) class ResourceManagerAccessibleByPrincipalSetFindsAllOwnedlements( FixtureHelper, TestCaseWithInvariants): invariants = dict( owner=dict( user=lambda self: self.getUniqueUser(), group=lambda self: self.getUniqueGroup(), ), ) def __hash__(self): return hash(repr(self)) def test(self): self.add_resources(["a", "b", "c"], owner=self.owner, is_public=True) self.add_resources(["x", "y", "z"], owner=self.owner, is_public=False) manager = ExampleRestrictedResource.objects resources = manager.accessible_by_principal(self.owner) self.assertEqual( [res.name for res in resources], ["a", "b", "c", "x", "y", "z"]) class ResourceManagerAccessibleSetTests( FixtureHelper, TestCase): def __hash__(self): return hash(repr(self)) def setUp(self): super(ResourceManagerAccessibleSetTests, self).setUp() self.user = self.getUniqueUser() self.unrealted_group = self.getUniqueGroup() self.group = self.getUniqueGroup() self.unrelated_user = self.getUniqueUser() self.manager = ExampleRestrictedResource.objects def add_resources(self, resources, owner, public): for name in resources: ExampleRestrictedResource.objects.create( owner=owner, name=name, is_public=public) def test_accessible_by_anyone_returns_only_public_objects(self): self.add_resources(["a", "b", "c"], owner=self.user, public=True) self.add_resources(["x", "y", "z"], owner=self.user, public=False) resources = ExampleRestrictedResource.objects.accessible_by_anyone() self.assertEqual( [res.name for res in resources], ["a", "b", "c"]) def test_accessible_by_prinipal_for_owner(self): self.add_resources(["a", "b", "c"], owner=self.user, public=True) self.add_resources(["x", "y", "z"], owner=self.user, public=False) resources = ExampleRestrictedResource.objects.accessible_by_principal( self.user) self.assertEqual( [res.name for res in resources], ["a", "b", "c", "x", "y", "z"]) def test_accessible_by_prinipal_for_group(self): self.add_resources(["a", "b", "c"], owner=self.group, public=True) self.add_resources(["x", "y", "z"], owner=self.group, public=False) resources = ExampleRestrictedResource.objects.accessible_by_principal( self.group) self.assertEqual( [res.name for res in resources], ["a", "b", "c", "x", "y", "z"]) def test_accessible_by_prinicpal_for_unrelated_user(self): self.add_resources(["a", "b", "c"], owner=self.user, public=True) self.add_resources(["x", "y", "z"], owner=self.user, public=False) resources = ExampleRestrictedResource.objects.accessible_by_principal( self.unrelated_user) self.assertEqual( [res.name for res in resources], ["a", "b", "c"]) def test_accessible_by_prinicpal_for_unrelated_user_without_any_public_objects(self): self.add_resources(["x", "y", "z"], owner=self.user, public=False) resources = self.manager.accessible_by_principal(self.unrelated_user) self.assertEqual([res.name for res in resources], []) def test_accessible_by_principal_for_group_and_member(self): self.add_resources(["a", "b", "c"], owner=self.group, public=True) self.add_resources(["x", "y", "z"], owner=self.group, public=False) self.user.groups.add(self.group) resources = self.manager.accessible_by_principal(self.user) self.assertEqual( [res.name for res in resources], ["a", "b", "c", "x", "y", "z"])