> This Software is in **Beta Version**, Please place an issue or contact me if you found a Bug !
# Flask-Roleman
**flask-roleman** is a flask extension for User Authorizations, Users can have Groups,
and Each Group can have Roles, you can define your Groups Model and Roles Model, as well as Users Model.
### About
- **Dependencies**: `flask` `flask-login`, `flask-sqlalchemy`
- **License**: Open Source Under **GNU GPL v2**
- **Author**: Mohamed El-Hasnaouy `codeberg.org/elhasnaouymed`
## Live Example
[**Jump to live example**](#the-live-example)
## Install
#### Using PIP
You can install **flask-roleman** using **pip**:
```shell
pip install flask-roleman
```
#### From source
Or you can download & compile & install it from source:
1. `git clone https://codeberg.org/Elhasnaouymed/flask-roleman.git`
2. `cd flask-roleman`
3. `python setup.py sdist`
4. `pip install dist/flask-roleman-*.tar.gz`
> Note: on most GNU/Linux distributions, You can install **only** inside a Virtual Environment (see [PEP 0668](https://peps.python.org/pep-0668/))
## Initialization
As most Extensions of Flask, you first import and create the Main Instance, then you can **Initialize** it in place
or after:
```python
...
db = SQLAlchemy()
roleman = RoleMan()
...
roleman.init_db(db, create_secondaries=True)
...
```
But before initialization, you **must** define the three models to inherit from their mixing,
as shown in the [next section](#usage).
also you should initialize before `db.init_app()` to allow secondary tables to be created.
### Mixing
To use **RoleMan** in your project, you need to have:
- **User Model** that inherits from `flask_roleman.UserModelMixing`.
- **Group Model** that inherits from `flask_roleman.GroupModelMixing`.
- **Role Model** that inherits from `flask_roleman.RoleModelMixing`.
> Group Model must have:
> - A String column `name`.
> - `users`: *many-to-many* relationship to the Users model, with `secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME` and `backref="groups"`.
> - `roles`: *many-to-many* relationship to the Roles model, with `secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME` and `backref="groups"`.
> Role Model must have:
> - A String column `name`.
### Minimal Example
Minimal Example of defining the three necessary Models with their Inheritance:
```python
from flask_roleman import RoleMan, UserModelMixing, GroupModelMixing, RoleModelMixing
class User(db.Model, UserModelMixing):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String, nullable=False, unique=True)
password = db.Column(db.String, nullable=False)
...
class Group(db.Model, GroupModelMixing):
__tablename__ = 'group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
users = db.relationship('User', secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME, backref="groups")
roles = db.relationship('Role', secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME, backref="groups")
...
class Role(db.Model, RoleModelMixing):
__tablename__ = 'role'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
...
```
# Usage
There are two ways to use this tool, [One](#static-authorizing) by decorating the route you want to protect,
[Two](#dynamic-authorizing) is by using `.has_roles()` method over a User or a Group
Both methods take variable length argument `*roles=Tuple[Union[List[str], str]]`, you can pass a list of roles like `['admin', 'manager', 'chief']`
or just a role name as string like `'admin'`
> * by passing a list, the User must have at least one of the Roles
> * by passing a string, the User must have that Role
>
> In other words: the program performs *AND* operator over the method arguments, and *OR* over the list values.
> **Notes**
>> You should assign Roles to Groups, and Groups to the Users.
>
>> The user get Authorized only if he has at least one of the Requested roles bound to one of his Groups.
## Static Authorizing
Whenever you want to **Require a role** from User, use this Decorator:
```python
from flask_roleman import roles_required
@app.route('/admin')
@roles_required('admin')
def admin_page():
...
return render_template('admin.html')
```
## Dynamic Authorizing
Or you can check dynamically using `current_user.has_roles`:
```python
from flask_login import current_user
from flask import abort
@app.route('/admin')
def admin_page():
if not current_user.has_roles('admin'):
return abort(401)
return render_template('admin.html')
```
# The Live Example
**In This example, the user will always get 401 error when accessing `/admin`,
until he logs in with a user that has the 'admin' role in one of his groups**
```python
from flask import Flask
from flask_login import LoginManager
from flask_roleman import RoleMan, UserModelMixing, GroupModelMixing, RoleModelMixing, roles_required
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = '5d19362626f3290221a2b37f0a5038d07e5aa0e18a9967ffcbedc69eaee4cce9'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.db'
db = SQLAlchemy()
login_manager = LoginManager(app)
@login_manager.user_loader
def user_loader(id: int):
return User.query.get(id)
class User(db.Model, UserModelMixing):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String, nullable=False, unique=True)
password = db.Column(db.String, nullable=False)
class Group(db.Model, GroupModelMixing):
__tablename__ = 'group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
users = db.relationship('User', secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME, backref="groups")
roles = db.relationship('Role', secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME, backref="groups")
class Role(db.Model, RoleModelMixing):
__tablename__ = 'role'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
@app.route('/')
def home():
return 'Hello World! from Home'
@app.route('/admin')
@roles_required('admin')
def admin():
return 'Admin Page !!!'
if __name__ == '__main__':
app.run(debug=True)
```
Raw data
{
"_id": null,
"home_page": "",
"name": "flask-roleman",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "flask,extension,roles,authorization,permissions,security,groups,privileges",
"author": "Mohamed El-Hasnaouy",
"author_email": "<elhasnaouymed@proton.me>",
"download_url": "https://files.pythonhosted.org/packages/f0/13/e0f6122dff8aecbc8651d02bd3391bc6a9d80bb36df051d1bf96690c7580/flask-roleman-1.0.2.tar.gz",
"platform": null,
"description": "\n> This Software is in **Beta Version**, Please place an issue or contact me if you found a Bug !\n\n# Flask-Roleman\n\n**flask-roleman** is a flask extension for User Authorizations, Users can have Groups, \nand Each Group can have Roles, you can define your Groups Model and Roles Model, as well as Users Model.\n\n### About\n\n- **Dependencies**: `flask` `flask-login`, `flask-sqlalchemy`\n- **License**: Open Source Under **GNU GPL v2**\n- **Author**: Mohamed El-Hasnaouy `codeberg.org/elhasnaouymed`\n\n## Live Example\n\n[**Jump to live example**](#the-live-example)\n\n## Install\n\n#### Using PIP\n\nYou can install **flask-roleman** using **pip**:\n\n```shell\npip install flask-roleman\n```\n\n#### From source\n\nOr you can download & compile & install it from source:\n\n1. `git clone https://codeberg.org/Elhasnaouymed/flask-roleman.git`\n2. `cd flask-roleman`\n3. `python setup.py sdist`\n4. `pip install dist/flask-roleman-*.tar.gz`\n\n> Note: on most GNU/Linux distributions, You can install **only** inside a Virtual Environment (see [PEP 0668](https://peps.python.org/pep-0668/))\n\n## Initialization\n\nAs most Extensions of Flask, you first import and create the Main Instance, then you can **Initialize** it in place\nor after:\n\n```python\n...\n\ndb = SQLAlchemy()\nroleman = RoleMan()\n\n...\n\nroleman.init_db(db, create_secondaries=True)\n\n...\n```\n\nBut before initialization, you **must** define the three models to inherit from their mixing,\nas shown in the [next section](#usage).\n\nalso you should initialize before `db.init_app()` to allow secondary tables to be created.\n\n### Mixing\n\nTo use **RoleMan** in your project, you need to have:\n - **User Model** that inherits from `flask_roleman.UserModelMixing`.\n - **Group Model** that inherits from `flask_roleman.GroupModelMixing`.\n - **Role Model** that inherits from `flask_roleman.RoleModelMixing`.\n\n> Group Model must have:\n> - A String column `name`.\n> - `users`: *many-to-many* relationship to the Users model, with `secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME` and `backref=\"groups\"`.\n> - `roles`: *many-to-many* relationship to the Roles model, with `secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME` and `backref=\"groups\"`.\n\n> Role Model must have:\n> - A String column `name`.\n\n### Minimal Example\nMinimal Example of defining the three necessary Models with their Inheritance:\n\n```python\nfrom flask_roleman import RoleMan, UserModelMixing, GroupModelMixing, RoleModelMixing\n\nclass User(db.Model, UserModelMixing):\n __tablename__ = 'user'\n id = db.Column(db.Integer, primary_key=True)\n email = db.Column(db.String, nullable=False, unique=True)\n password = db.Column(db.String, nullable=False)\n ...\n\nclass Group(db.Model, GroupModelMixing):\n __tablename__ = 'group'\n id = db.Column(db.Integer, primary_key=True)\n name = db.Column(db.String, nullable=False, unique=True)\n users = db.relationship('User', secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME, backref=\"groups\")\n roles = db.relationship('Role', secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME, backref=\"groups\")\n ...\n \nclass Role(db.Model, RoleModelMixing):\n __tablename__ = 'role'\n id = db.Column(db.Integer, primary_key=True)\n name = db.Column(db.String, nullable=False, unique=True)\n ...\n```\n\n# Usage\n\nThere are two ways to use this tool, [One](#static-authorizing) by decorating the route you want to protect, \n[Two](#dynamic-authorizing) is by using `.has_roles()` method over a User or a Group\n\nBoth methods take variable length argument `*roles=Tuple[Union[List[str], str]]`, you can pass a list of roles like `['admin', 'manager', 'chief']`\nor just a role name as string like `'admin'`\n\n> * by passing a list, the User must have at least one of the Roles\n> * by passing a string, the User must have that Role\n> \n> In other words: the program performs *AND* operator over the method arguments, and *OR* over the list values.\n\n\n> **Notes**\n>> You should assign Roles to Groups, and Groups to the Users.\n> \n>> The user get Authorized only if he has at least one of the Requested roles bound to one of his Groups. \n\n## Static Authorizing\n\nWhenever you want to **Require a role** from User, use this Decorator:\n\n```python\nfrom flask_roleman import roles_required\n\n@app.route('/admin')\n@roles_required('admin')\ndef admin_page():\n ...\n return render_template('admin.html')\n```\n\n## Dynamic Authorizing\n\nOr you can check dynamically using `current_user.has_roles`:\n\n```python\nfrom flask_login import current_user\nfrom flask import abort\n\n@app.route('/admin')\ndef admin_page():\n if not current_user.has_roles('admin'):\n return abort(401)\n return render_template('admin.html')\n```\n\n\n# The Live Example\n\n**In This example, the user will always get 401 error when accessing `/admin`,\nuntil he logs in with a user that has the 'admin' role in one of his groups**\n\n```python\nfrom flask import Flask\nfrom flask_login import LoginManager\nfrom flask_roleman import RoleMan, UserModelMixing, GroupModelMixing, RoleModelMixing, roles_required\nfrom flask_sqlalchemy import SQLAlchemy\n\napp = Flask(__name__)\napp.config['SECRET_KEY'] = '5d19362626f3290221a2b37f0a5038d07e5aa0e18a9967ffcbedc69eaee4cce9'\napp.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.db'\ndb = SQLAlchemy()\nlogin_manager = LoginManager(app)\n\n\n@login_manager.user_loader\ndef user_loader(id: int):\n return User.query.get(id)\n\n\nclass User(db.Model, UserModelMixing):\n __tablename__ = 'user'\n id = db.Column(db.Integer, primary_key=True)\n email = db.Column(db.String, nullable=False, unique=True)\n password = db.Column(db.String, nullable=False)\n\n\nclass Group(db.Model, GroupModelMixing):\n __tablename__ = 'group'\n id = db.Column(db.Integer, primary_key=True)\n name = db.Column(db.String, nullable=False, unique=True)\n users = db.relationship('User', secondary=RoleMan.SECONDARY_USER_GROUP_TABLE_NAME, backref=\"groups\")\n roles = db.relationship('Role', secondary=RoleMan.SECONDARY_GROUP_ROLE_TABLE_NAME, backref=\"groups\")\n\n\nclass Role(db.Model, RoleModelMixing):\n __tablename__ = 'role'\n id = db.Column(db.Integer, primary_key=True)\n name = db.Column(db.String, nullable=False, unique=True)\n\n\n@app.route('/')\ndef home():\n return 'Hello World! from Home'\n\n@app.route('/admin')\n@roles_required('admin')\ndef admin():\n return 'Admin Page !!!'\n\n\nif __name__ == '__main__':\n app.run(debug=True)\n```\n",
"bugtrack_url": null,
"license": "",
"summary": "Flask Extension for User Authorizations, Users can have Groups, and Each Group can have Roles, more control and easy to use.",
"version": "1.0.2",
"project_urls": null,
"split_keywords": [
"flask",
"extension",
"roles",
"authorization",
"permissions",
"security",
"groups",
"privileges"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "f013e0f6122dff8aecbc8651d02bd3391bc6a9d80bb36df051d1bf96690c7580",
"md5": "115e4fbc0e010f75f2af7f7c2ee0dec2",
"sha256": "c6263559edcc4abf524a84a6f79eae129133ac1bb388386d6767e18aecbd6fad"
},
"downloads": -1,
"filename": "flask-roleman-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "115e4fbc0e010f75f2af7f7c2ee0dec2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5634,
"upload_time": "2023-12-05T21:35:07",
"upload_time_iso_8601": "2023-12-05T21:35:07.463290Z",
"url": "https://files.pythonhosted.org/packages/f0/13/e0f6122dff8aecbc8651d02bd3391bc6a9d80bb36df051d1bf96690c7580/flask-roleman-1.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-12-05 21:35:07",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "flask-roleman"
}