{ "info": { "author": "Paylogic International", "author_email": "developers@paylogic.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing", "Topic :: Utilities" ], "description": "Balrog\n======\n\nBalrog is a Python library that helps you to build an authorization system in your projects:\n\n::\n\n You shall not pass!\n\n\nBalrog is good for systems with statically defined roles that enable certain workflows.\nEvery identity can have only one role on the certain context. This approach allows covering\nyour system with functional tests according to the roles and flows these roles perform.\nFormal requirements can be applied to the workflows in the system which will define roles.\n\nThese roles are statically defined in the code and this way properly versioned and covered\nwith testing. It is possible to do a composition of certain permission groups and share them\nbetween roles, but semantically there's no way for one identity to have 2 contradicting\nroles when one role forbids actions and the other allows them. Instead a proper role\ncan be extracted with the permissions it allows.\n\n\nInstallation\n------------\n\n.. code-block:: sh\n\n pip install balrog\n\n\nUsage\n-----\n\nPermission is needed to access a resource or to perform an action. Permissions are grouped in the roles\nand roles are grouped in the policies.\n\nThe entry point where a permission is being checked is the Policy. Define an instance of the Policy\nand specify the list of roles it works with.\n\nProject can contain multiple policies that serve different purposes.\n\nPermission declaration:\n\n.. code-block:: python\n\n\n import balrog\n from flask import request\n\n def get_identity(*args, **kwargs):\n \"\"\"Get current user.\"\"\"\n # Flask request wrapper implements the ``user`` property\n return request.user\n\n def get_role(identity, *args, **kwargs):\n \"\"\"Get current identity role.\"\"\"\n # User.role is a property of the ORM User model\n return identity.role\n\n\n read = balrog.Permissions(name=\"article.read\")\n post = balrog.Permissions(name=\"article.post\")\n comment = balrog.Permissions(name=\"article.comment\")\n\n anonymous = balrog.Role(\n name=\"anonymous\",\n permissions=[read],\n )\n \"\"\"Anonymous visitors can read articles.\"\"\"\n\n user = balrog.Role(\n name=\"user\",\n permissions=[read, comment],\n )\n \"\"\"User accounts can read and comment articles.\"\"\"\n\n author = balrog.Role(\n name=\"author\",\n permissions=[read, post, comment],\n )\n \"\"\"Author accounts can read, create and comment articles.\"\"\"\n\n policy = balrog.Policy(\n roles=[anonymous, user, author],\n get_identity=get_identity,\n get_role=get_role,\n permissions=[read, post, comment]\n )\n\n\nPermission checking:\n\n.. code-block:: python\n\n # ...\n policy = balrog.Policy(roles=[anonymous, user, author], get_identity=get_identity, get_role=get_role)\n policy.check(\"article.comment\")\n\n\nFiltering collections:\n\n.. code-block:: python\n\n articles = session.query(Article)\n my_articles = policy.filter(\"article.view\", objects=articles)\n\n\nEvery role is a collection of permissions. Besides being included in the role permissions can\nimplement even more detailed checking and filtering logic.\n\n\nPermission\n----------\n\nPermissions have unique names (within the role) which reflect the resource and the action you\nwant to take with this resource.\n\n.. code-block:: python\n\n import balrog\n\n eat = balrog.Permission(name=\"cucumber.eat\")\n happy = balrog.Permission(name=\"be-happy\")\n\n\nName is just a string identifier that you are using in order to ask a policy for a permission.\nThe name formatting convention can be decided per project.\n\nPermissions have 2 methods: ``check`` and ``filter``. By default the ``check`` method implements ``True``\nand the ``filter`` method is simply bypassing the objects. These methods are an additional opportunities\nto control the access to certain context, instances of your resources, check whitelists, filter out objects\nfrom collections that can not be seen by currently authenticated identity, etc.\n\n\nRole\n----\n\nRoles have unique names within the policy. Role name is determined by the authenticated identity\nand used in the policy permission check implicitly.\n\nRoles are collections of permissions that define the role and enable certain workflows in your\nsystem.\n\nWhen a system is large and has a lot of specific permissions declared sometimes it is easier to\nsubclass the Role class instead of granting all permissions to the role:\n\n.. code-block:: python\n\n import balrog\n\n\n class Admin(balrog.Role):\n\n def check(self, identity, permission, *args, **kwargs):\n return True\n\n\n\nPolicy\n------\n\nPolicy is used as an entry point of permission checking in your project. It incapsulates the roles\nthat define your workflows. There could be multiple policy instances in the project.\n\nBesides roles policy requires some configuration and backend implementation:\n\nget_identity\n~~~~~~~~~~~~\n\nA callback that returns currenlty authenticated identity. Projects have to implement this backend\nand restore the identity instance (e.g. User object) for example from the Flask Request object.\n\n.. code-block:: python\n\n from flask import request\n\n def get_identity(*args, **kwargs):\n \"\"\"Get current user.\"\"\"\n # Flask request wrapper implements the ``user`` property\n return request.user\n\n\n\nget_role\n~~~~~~~~\n\nA callback that returns which role current identity has on the context. In the simple case the role is associated\nto the user in the database.\n\n\n.. code-block:: python\n\n def get_role(identity, *args, **kwargs):\n \"\"\"Get current identity role.\"\"\"\n # User.role is a property of the ORM User model\n return identity.role\n\n\ncheck\n~~~~~\n\nThe permission check. All arguments that you pass to this function are passed along in Role.check and finally\nto Permission.check.\n\n.. code-block:: python\n\n if not policy.check(\"article.read\", article=a):\n flask.abort(\"You can't access the article `{0}`\".format(a.id))\n\nfilter\n~~~~~~\n\nThe function that is filtering out items of the given objects if the identity has no permission to access them.\n\n\n.. code-block:: python\n\n articles = session.query(Article).filter_by(is_published=True)\n\n my_articles = policy.filter(\"article.read\", objects=articles)\n\n\nImplementing your own filtering:\n\n.. code-block:: python\n\n import balrog\n\n class ViewArticle(balrog.Permission);\n\n def filter(self, identity, objects, *args, **kwargs):\n \"\"\"Filter out articles of the other users.\n\n :param identity: User object.\n :param objects: SQLAlchemy query.\n\n :returns: SQLAlchemy query with applied filtering.\n \"\"\"\n return objects.filter_by(user_id=identity.id)\n\n\nFilter function can raise an exception in the case when there's no such permission\nin the role of the identity. In this case the library doesn't know for sure what type to\nreturn that represents an empty collection of objects. Some projects would expect\nan empty list, some - falsy ORM query, etc. Instead the exception should be handled:\n\n\n.. code-block:: python\n\n try:\n my_articles = policy.filter(\"article.read\", objects=articles)\n except balrog.PermissionNotFound:\n my_articles = []\n\n\ncontext\n~~~~~~~\n\nEverything that you pass extra to the check or filter function is passed along to the regarding\nRole and Permission methods.\nYou can pass certain instance of an object you control your access using whitelists.\n\n.. code-block:: python\n\n policy.check(\"message.send\", ip=ip_addr)\n\n\nPolicy.check method can compare if ip address is in a whitelist.\n\n\nContact\n-------\n\nIf you have questions, bug reports, suggestions, etc. please create an issue on\nthe `GitHub project page `_.\n\n\nLicense\n-------\n\nThis software is licensed under the `MIT license `_\n\nSee `License `_\nChangelog\n=========\n\n1.1.0\n-----\n\n- Policy will keep track of all the permissions and raise PermissionNotFound when called with unknown permission (hvdklauw)\n\n1.0.1\n-----\n\n- Pass the context to the get_identity (olegpidsadnyi)\n\n1.0.0\n-----\n\n* Initial public release", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/paylogic/balrog", "keywords": null, "license": "MIT license", "maintainer": null, "maintainer_email": null, "name": "balrog", "package_url": "https://pypi.org/project/balrog/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/balrog/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/paylogic/balrog" }, "release_url": "https://pypi.org/project/balrog/1.1.0/", "requires_dist": null, "requires_python": null, "summary": "Python access control library.", "version": "1.1.0" }, "last_serial": 1735941, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "ae1937af55fb6b14e91dbec77281fd0a", "sha256": "a304d4bc3a34fc6b641f74954c52b497d76686c7e5f0d3d38a6635d4479ff054" }, "downloads": -1, "filename": "balrog-1.0.0.tar.gz", "has_sig": false, "md5_digest": "ae1937af55fb6b14e91dbec77281fd0a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6612, "upload_time": "2014-09-24T08:46:57", "url": "https://files.pythonhosted.org/packages/eb/d9/1d1defd99089116e88a0d5dac4128d45805259807a692924b2a46a5dd418/balrog-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "4a726c39b6a540fe6e2d5520bcee8fc8", "sha256": "7db32b41ef6971b52fe46875d93ca9f4d591336da1b1ba44533387135d852bf1" }, "downloads": -1, "filename": "balrog-1.0.1.tar.gz", "has_sig": false, "md5_digest": "4a726c39b6a540fe6e2d5520bcee8fc8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7203, "upload_time": "2014-09-24T12:46:02", "url": "https://files.pythonhosted.org/packages/d1/eb/a35bc802ffdc30a1c0387401aef0bb16e5143d54d5aba50a290394c9ea59/balrog-1.0.1.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "b46db514ae4ee7fb3d96bd1b2fc09347", "sha256": "456e08cdb028d02f19cb61cd45fe06a51cd988ff45018cb5398da34cfcab336a" }, "downloads": -1, "filename": "balrog-1.1.0.tar.gz", "has_sig": false, "md5_digest": "b46db514ae4ee7fb3d96bd1b2fc09347", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7223, "upload_time": "2015-09-24T08:28:42", "url": "https://files.pythonhosted.org/packages/c3/6f/87977bd309d0f69a8a2c138077a172477fa17fc15fe2ac880fa079b70ea4/balrog-1.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b46db514ae4ee7fb3d96bd1b2fc09347", "sha256": "456e08cdb028d02f19cb61cd45fe06a51cd988ff45018cb5398da34cfcab336a" }, "downloads": -1, "filename": "balrog-1.1.0.tar.gz", "has_sig": false, "md5_digest": "b46db514ae4ee7fb3d96bd1b2fc09347", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7223, "upload_time": "2015-09-24T08:28:42", "url": "https://files.pythonhosted.org/packages/c3/6f/87977bd309d0f69a8a2c138077a172477fa17fc15fe2ac880fa079b70ea4/balrog-1.1.0.tar.gz" } ] }