{"id":13421581,"url":"https://github.com/anurag90x/flask-pundit","last_synced_at":"2025-03-15T10:31:14.480Z","repository":{"id":48683460,"uuid":"49325437","full_name":"anurag90x/flask-pundit","owner":"anurag90x","description":"A flask extension for managing permissions and scopes","archived":false,"fork":false,"pushed_at":"2023-05-01T20:19:26.000Z","size":1326,"stargazers_count":52,"open_issues_count":3,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T14:14:36.147Z","etag":null,"topics":["authorization","flask-extension","permissions"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anurag90x.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-01-09T13:33:01.000Z","updated_at":"2024-10-16T11:42:46.000Z","dependencies_parsed_at":"2023-10-20T17:29:43.222Z","dependency_job_id":null,"html_url":"https://github.com/anurag90x/flask-pundit","commit_stats":{"total_commits":74,"total_committers":5,"mean_commits":14.8,"dds":"0.14864864864864868","last_synced_commit":"d83af6a2958920b85a7015dd35f486d7de76dd15"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anurag90x%2Fflask-pundit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anurag90x%2Fflask-pundit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anurag90x%2Fflask-pundit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anurag90x%2Fflask-pundit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anurag90x","download_url":"https://codeload.github.com/anurag90x/flask-pundit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243718892,"owners_count":20336590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authorization","flask-extension","permissions"],"created_at":"2024-07-30T23:00:25.587Z","updated_at":"2025-03-15T10:31:14.050Z","avatar_url":"https://github.com/anurag90x.png","language":"Python","funding_links":[],"categories":["介绍","Authorization"],"sub_categories":[],"readme":"# Flask-Pundit [![Build Status](https://travis-ci.org/anurag90x/flask-pundit.svg?branch=master)](https://travis-ci.org/anurag90x/flask-pundit)  \nA simple flask extension to organize resource authorization and scoping. This extension is heavily inspired by the ruby Pundit library.\n\n## Installation\n` pip install flask-pundit `\n\n## Initialization\n\nYou can initialize the extension in one of 2 ways - \n\n1. `pundit = FlaskPundit(app)` where app is the application object.\n2. `pundit.init_app(app)` after constructing the FlaskPundit object without an app object. \n\nWhen initializing the extension, you can provide an optional `policies_path` parameter which tells Flask-Pundit where to find your policy classes. If no value is specified this defaults to `policies`.\n\nWhat is this `policies_path` exactly?\n\nFlask-Pundit expects you to have 1 policy per model class. To find the Policy for a particular model it needs to know where to look. That is the `policies_path`. \n\n## Policies\n\nA policy class defines the 'rules' used to authorize a model. You can write your own policy class as follows:\n\n```python\nclass PostPolicy():\n        def __init__(self, user, post):\n                self.user = user\n                self.post = post\n        \n        def get(self):\n                return self.user == 'admin' and self.post.id == 1\n```\nThe user object is the currently 'logged' in user and the post object is the model instance you want to authorize.\nThe `get` method is an authorization 'action' handler that you might want to execute when a user is trying to read a post.\n\nYou could alternatively define your own `BasePolicy` class and extend it in a similar fashion or use the `ApplicationPolicy` class provided by the extension in which case the code would be:\n\n```python\nfrom flask_pundit.application_policy import ApplicationPolicy\n\nclass PostPolicy(ApplicationPolicy):\n        def get(self):\n                return self.user == 'admin' and self.record.id == 1\n```\nNote that now we're using `record` inside the method. By inheriting from `ApplicationPolicy` all instance methods now use `record` to represent the model instance being authorized.\n\nTo authorize a post object inside a resource (or a blueprint or just a app.route decorated function) you would call `self.pundit.authorize(post)`. This will cause flask-pundit to look for the `PostPolicy` class at `policies/post`. If you want a different root to be searched, you can specify the ` policies_path` when initializing the extension.\n\nThis example shows how to use the authorize method in a single module app.\n\n```python\napp = Flask('blog_series')\npundit = FlaskPundit(app)\n\n@app.route('/blogs/\u003cid\u003e')\ndef read_blog_post(id):\n        blog = Post.get_by_id(id)\n        if pundit.authorize(post):\n                return blog\n        return ForbiddenError, 403\n```\nThe authorize method takes 3 parameters:\n\n1. A record - This can be either an object or class and corresponds to a 'model' that you're doing the authorization on.\n\n2. An action - This corresponds to the policy method that you want to invoke for doing the authorization. If no value is provided it\ndefaults to `request.method.lowercase()`. Thus in the previous snippet the `get` method of a `BlogPolicy` object would be invoked.\n\n3. A user - This is akin to the currently 'logged in' user. If no user object is provided, flask-pundit tries to pick either `flask.g.user` or \n`flask.g.current_user`, whichever is available.\n\nThus in the above set of examples, invoking `authorize` executes the `get` method in the `PostPolicy` class at `policies/post` with the record being the `post` object filtered by id.\n\n## Scopes\n\nThe `authorize` method acts more as a true/false guard. On the other hand the `policy_scope` method returns a 'scoped' version of a model. For example, if you have a page with all posts, you might want to let an admin see all of them but restrict the ones staff users see. This is where you'd want to use `policy_scope` instead of `authorize`.\n\nTo do so, you need to define a `scope` method in your policy.\n\n```python\nfrom flask_pundit.application_policy import ApplicationPolicy\n\nclass PostPolicy(ApplicationPolicy):\n        def get(self):\n                return self.user == 'admin' and self.record.id == 1\n\n\n        def scope(self):\n                if self.user == 'admin':\n                        return record.all()\n                return record.filter_by(author='staff')\n        \n```\n\nWhen you call the `policy_scope(model)` with a model class (it doesn't make sense to pass an object here), the `scope` method gets called.\n\n``` python\nfrom app import pundit\n\n@app.route('/posts)\ndef index():\n        all_posts = pundit.policy_scope(Post)\n        return all_posts\n```\nThe examples here show how to return all posts for an admin and only staff posts for a staff user.\n\nThe `policy_scope` method takes 2 arguments:\n\n1. A model - This is the class that is to be 'scoped'.\n\n2. A user object - This is just like the user object in the authorize case.\n\n## Verification\n\nFlask-Pundit has 2 decorators you can use to verify `authorize`/ `policy_scope` has been called. They are `verify_authorized` and `verify_policy_scoped`.\n\nIn a single module app you would use `verify_authorized` as:\n\n``` python\nfrom flask_pundit import verify_authorized\nfrom app import app, pundit\n\n@app.route('/posts/\u003cid\u003e')\n@verify_authorized\ndef read_blog_post(id):\n        blog_post = Post.get_by_id(id)\n        if pundit.authorize(blog_post):\n                return blog_post\n        return ForbiddenError, 403\n```\nIf you remove the call to `authorize` the decorator will throw a `RuntimeError` as it expects a call but found none.\n\nThe `verify_policy_scoped` decorator would be used in the exact same way. Using these 2 would prove more useful if you're using something like [Flask-Restful](https://github.com/flask-restful/flask-restful) where you could specify these as `method_decorators` in your resource, if you wanted all the methods to be verified.\n\nIf you prefer not using decorators you could use `pundit._verify_authorized` and `pundit._verify_policy_scoped` directly inside your methods. Calling them directly will return `True` or `False`.\n\n## Custom Policy class\n\nYou could override the policy class lookup behaviour by adding a `__policy_class__` property on your models. This should reference the class that you want to be used against this model. For example,\n\n```python\nfrom policies.commenting import CommentingPolicy\n\nclass Comment:\n        __policy_class__ = CommentingPolicy\n```\nNow when doing either `authorize` or `policy_scope` against an instance of `Comment` or the class itself, `CommentingPolicy` will be used.\n\n## License\n\nLicensed under MIT license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanurag90x%2Fflask-pundit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanurag90x%2Fflask-pundit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanurag90x%2Fflask-pundit/lists"}