{ "info": { "author": "garenchan", "author_email": "1412950785@qq.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy" ], "description": "# Policy\nA Policy library provides support for RBAC policy enforcement.\n\n\n## Preface\n\nWhen I used ``Flask`` to write a ``RESTful web service``, I didn't find a suitable extension to handle endpoints' permission control. Because I really like the permission control method of ``OpenStack`` services which based on a policy file. So I want to implement a more generic library similar to ``oslo.policy``.\n\n\n## Demo\n\n### Generate Policy File\n\nSuppose there are two roles: **user** and **admin**, and two resources: **article** and **user**. We have 3 policies:\n\n- Only user can update article\n- Creating new user requires admin permission.\n- Only article owners or admin-role user can delete articles.\n\nBased on the previous description, we generate the following policy file ``policy.json``:\n\n {\n \"is_admin\": \"role:admin\",\n \"is_user\": \"role:user or role:admin\",\n\n \"article:update\": \"rule:is_user\",\n \"article:delete\": \"role:admin or id:%(user_id)s\",\n \"user:create\": \"rule:is_admin\"\n }\n\n\n### Enforce Policy With Flask Application\n\nSuppose we have a simple ``Flask`` application which provides two api: creating new user and deleting article and we run it:\n\n```\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nimport functools\n\nfrom flask import Flask, request, g\n\nfrom policy import Enforcer\nfrom policy.exceptions import PolicyNotAuthorized\n\napp = Flask(__name__)\nenforcer = Enforcer('policy.json', raise_error=True)\n\n\n@app.errorhandler(PolicyNotAuthorized)\ndef handle_policy_exception(error):\n return str(error)\n\n\nusers = {\n 'lily': {\n 'id': 'd55a4192eb3b489589d5ee95dcf3af7d',\n 'roles': ['user', 'admin']\n },\n 'kate': {\n 'id': '1a535309687244e2aa434b25ef4bfb59',\n 'roles': ['user']\n },\n 'lucy': {\n 'id': '186977181e7f4a9e85104ca017e845f3',\n 'roles': ['user']\n }\n}\n\narticles = {\n 'python': {\n 'id': 'e6e31ad693734b269099d9acac2cb800',\n 'user_id': '1a535309687244e2aa434b25ef4bfb59' # owned by kate\n }\n}\n\n\ndef login_required(func):\n @functools.wraps(func)\n def wrapped(*args, **kwargs):\n username = request.args.get('me')\n credential = users.get(username)\n if not credential:\n raise Exception('login required')\n else:\n g.cred = credential\n return func(*args, **kwargs)\n\n return wrapped\n\n\ndef enforce_policy(rule):\n \"\"\"Enforce a policy to a API.\"\"\"\n def wrapper(func):\n \"\"\"Decorator used for wrap API.\"\"\"\n @functools.wraps(func)\n def wrapped(*args, **kwargs):\n if enforcer.enforce(rule, {}, g.cred):\n return func(*args, **kwargs)\n\n return wrapped\n\n return wrapper\n\n\n@app.route('/user', methods=['GET'])\n@login_required\n@enforce_policy('user:create')\ndef create_user():\n # do create action here\n return 'user created'\n\n\n@app.route('/article', methods=['GET'])\n@login_required\ndef delete_article():\n article_name = request.args.get('name')\n article = articles.get(article_name)\n\n # do fine-grained permission check here\n enforcer.enforce('article:delete', article, g.cred)\n # do delete action here\n return 'arcticle %s deleted' % article['id']\n\n\nif __name__ == '__main__':\n app.run(port=8888, debug=True)\n```\n\n#### View-Level\n\nWe provide a ``enforce_policy`` decorator to enforce policy on views ``create_user``.\n\nWe head to http://127.0.0.1:8888/user?me=kate to simulate ``kate``'s creating user and get a error:\n\n user:create on {} by {'roles': ['user'], 'id': '1a535309687244e2aa434b25ef4bfb59'} disallowed by policy\n\nThen we head to http://127.0.0.1:8888/user?me=lily to simulate ``lily``'s creating user and get a successful response:\n\n user created\n\n#### Fine-Grained\n\nIn some scenarios we want a fine-grained permission check. We enforce policy inside view ``delete_article``, because outside of it we can't know which article the user wants to delete.\n\nWe head to http://127.0.0.1:8888/article?me=lucy&name=python to simulate ``lucy``'s deleting article and get a error:\n\n article:delete on {'user_id': '1a535309687244e2aa434b25ef4bfb59', 'id': 'e6e31ad693734b269099d9acac2cb800'} by {'roles': ['user'], 'id': '186977181e7f4a9e85104ca017e845f3'} disallowed by policy\n\nThen we head to http://127.0.0.1:8888/article?me=kate&name=python to simulate ``kate``'s deleting article and get a successful response because ``kate`` is the article's owner:\n\n arcticle e6e31ad693734b269099d9acac2cb800 deleted\n\nFinally we head to http://127.0.0.1:8888/article?me=lily&name=python to simulate ``lily``'s deleting article and get a successful response because ``lily`` is a admin-role user:\n\n arcticle e6e31ad693734b269099d9acac2cb800 deleted\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/garenchan/policy.git", "keywords": "", "license": "http://www.apache.org/licenses/LICENSE-2.0", "maintainer": "", "maintainer_email": "", "name": "policy", "package_url": "https://pypi.org/project/policy/", "platform": "", "project_url": "https://pypi.org/project/policy/", "project_urls": { "Homepage": "https://github.com/garenchan/policy.git" }, "release_url": "https://pypi.org/project/policy/1.0.0/", "requires_dist": null, "requires_python": "", "summary": "A Policy library provides support for RBAC policy enforcement.", "version": "1.0.0" }, "last_serial": 4244088, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "bd601154f53f2e3c315ca1e283dfd929", "sha256": "9d3c37761720657bed51fc7c608e88f4e666bea4acceed64159cffd70262c711" }, "downloads": -1, "filename": "policy-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "bd601154f53f2e3c315ca1e283dfd929", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11489, "upload_time": "2018-09-06T09:35:10", "url": "https://files.pythonhosted.org/packages/5e/61/284a7cd89afc4a84576a8f6a62be2545faa0e900e573cfbb8f747d12a4a3/policy-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "41c218ba3db230cf1b5691de0bfdbda1", "sha256": "66b747f621d74d60184f4155ad01510b81e150e15cd596a4de99d48e9fd2f182" }, "downloads": -1, "filename": "policy-1.0.0.zip", "has_sig": false, "md5_digest": "41c218ba3db230cf1b5691de0bfdbda1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21039, "upload_time": "2018-09-06T09:35:12", "url": "https://files.pythonhosted.org/packages/8c/fc/b9d9ea164165f704fe3ddf0f9d8f3b3f63fb96f7c9bfdcbeb7eb1c05b2a6/policy-1.0.0.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "bd601154f53f2e3c315ca1e283dfd929", "sha256": "9d3c37761720657bed51fc7c608e88f4e666bea4acceed64159cffd70262c711" }, "downloads": -1, "filename": "policy-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "bd601154f53f2e3c315ca1e283dfd929", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11489, "upload_time": "2018-09-06T09:35:10", "url": "https://files.pythonhosted.org/packages/5e/61/284a7cd89afc4a84576a8f6a62be2545faa0e900e573cfbb8f747d12a4a3/policy-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "41c218ba3db230cf1b5691de0bfdbda1", "sha256": "66b747f621d74d60184f4155ad01510b81e150e15cd596a4de99d48e9fd2f182" }, "downloads": -1, "filename": "policy-1.0.0.zip", "has_sig": false, "md5_digest": "41c218ba3db230cf1b5691de0bfdbda1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21039, "upload_time": "2018-09-06T09:35:12", "url": "https://files.pythonhosted.org/packages/8c/fc/b9d9ea164165f704fe3ddf0f9d8f3b3f63fb96f7c9bfdcbeb7eb1c05b2a6/policy-1.0.0.zip" } ] }