Skip to main content
# Policy
A Policy library provides support for RBAC policy enforcement.


## Preface

When 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``.


## Demo

### Generate Policy File

Suppose there are two roles: **user** and **admin**, and two resources: **article** and **user**. We have 3 policies:

- Only user can update article
- Creating new user requires admin permission.
- Only article owners or admin-role user can delete articles.

Based on the previous description, we generate the following policy file ``policy.json``:

{
"is_admin": "role:admin",
"is_user": "role:user or role:admin",

"article:update": "rule:is_user",
"article:delete": "role:admin or id:%(user_id)s",
"user:create": "rule:is_admin"
}


### Enforce Policy With Flask Application

Suppose we have a simple ``Flask`` application which provides two api: creating new user and deleting article and we run it:

```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import functools

from flask import Flask, request, g

from policy import Enforcer
from policy.exceptions import PolicyNotAuthorized

app = Flask(__name__)
enforcer = Enforcer('policy.json', raise_error=True)


@app.errorhandler(PolicyNotAuthorized)
def handle_policy_exception(error):
return str(error)


users = {
'lily': {
'id': 'd55a4192eb3b489589d5ee95dcf3af7d',
'roles': ['user', 'admin']
},
'kate': {
'id': '1a535309687244e2aa434b25ef4bfb59',
'roles': ['user']
},
'lucy': {
'id': '186977181e7f4a9e85104ca017e845f3',
'roles': ['user']
}
}

articles = {
'python': {
'id': 'e6e31ad693734b269099d9acac2cb800',
'user_id': '1a535309687244e2aa434b25ef4bfb59' # owned by kate
}
}


def login_required(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
username = request.args.get('me')
credential = users.get(username)
if not credential:
raise Exception('login required')
else:
g.cred = credential
return func(*args, **kwargs)

return wrapped


def enforce_policy(rule):
"""Enforce a policy to a API."""
def wrapper(func):
"""Decorator used for wrap API."""
@functools.wraps(func)
def wrapped(*args, **kwargs):
if enforcer.enforce(rule, {}, g.cred):
return func(*args, **kwargs)

return wrapped

return wrapper


@app.route('/user', methods=['GET'])
@login_required
@enforce_policy('user:create')
def create_user():
# do create action here
return 'user created'


@app.route('/article', methods=['GET'])
@login_required
def delete_article():
article_name = request.args.get('name')
article = articles.get(article_name)

# do fine-grained permission check here
enforcer.enforce('article:delete', article, g.cred)
# do delete action here
return 'arcticle %s deleted' % article['id']


if __name__ == '__main__':
app.run(port=8888, debug=True)
```

#### View-Level

We provide a ``enforce_policy`` decorator to enforce policy on views ``create_user``.

We head to http://127.0.0.1:8888/user?me=kate to simulate ``kate``'s creating user and get a error:

user:create on {} by {'roles': ['user'], 'id': '1a535309687244e2aa434b25ef4bfb59'} disallowed by policy

Then we head to http://127.0.0.1:8888/user?me=lily to simulate ``lily``'s creating user and get a successful response:

user created

#### Fine-Grained

In 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.

We head to http://127.0.0.1:8888/article?me=lucy&name=python to simulate ``lucy``'s deleting article and get a error:

article:delete on {'user_id': '1a535309687244e2aa434b25ef4bfb59', 'id': 'e6e31ad693734b269099d9acac2cb800'} by {'roles': ['user'], 'id': '186977181e7f4a9e85104ca017e845f3'} disallowed by policy

Then 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:

arcticle e6e31ad693734b269099d9acac2cb800 deleted

Finally 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:

arcticle e6e31ad693734b269099d9acac2cb800 deleted


Metadata

Release files for policy 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for policy 1.0.0
File Size Uploaded Provenance Actions
policy-1.0.0.zip 21.0 kB Without provenance Details for policy-1.0.0.zip

Built distribution (wheel)

Table of built distributions (wheels) for policy 1.0.0
File Interpreter ABI Platform Actions
policy-1.0.0-py3-none-any.whl Python 3 none any Details for policy-1.0.0-py3-none-any.whl

Total release size: 32.5 kB

Release files / policy-1.0.0.zip

Download URL policy-1.0.0.zip
Size 21.0 kB
Tags Source
SHA-256 checksum
How to use checksums
66b747f621d74d60184f4155ad01510b81e150e15cd596a4de99d48e9fd2f182
BLAKE2b-256 checksum
How to use checksums
8cfcb9d9ea164165f704fe3ddf0f9d8f3b3f63fb96f7c9bfdcbeb7eb1c05b2a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.11.0 pkginfo/1.4.2 requests/2.18.4 setuptools/39.2.0 requests-toolbelt/0.8.0 tqdm/4.23.4 CPython/3.4.3

Release files / policy-1.0.0-py3-none-any.whl

Download URL policy-1.0.0-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9d3c37761720657bed51fc7c608e88f4e666bea4acceed64159cffd70262c711
BLAKE2b-256 checksum
How to use checksums
5e61284a7cd89afc4a84576a8f6a62be2545faa0e900e573cfbb8f747d12a4a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.11.0 pkginfo/1.4.2 requests/2.18.4 setuptools/39.2.0 requests-toolbelt/0.8.0 tqdm/4.23.4 CPython/3.4.3

Security for policy 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page