inuits-policy-based-auth


Nameinuits-policy-based-auth JSON
Version 3.0.0 PyPI version JSON
download
home_page
SummaryModule for securing API endpoints based on policies.
upload_time2023-04-17 16:34:18
maintainer
docs_urlNone
authorInuits
requires_python
licenseGPLv2
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # inuits_policy_based_auth
inuits_policy_based_auth is a python package for securing API endpoints based on policies.

## Installation
Install inuits_policy_based_auth as follows:
```
pip install inuits-policy-based-auth
```

## Getting Started
Instantiate the PolicyFactory in you app, for example in app.py (all examples given are based on a Python Flask app).
```python
from inuits_policy_based_auth import PolicyFactory


policy_factory = PolicyFactory()
```
### Manually loading policies
Importing and registering policies can be done manually. Don't forget to set a fallback key, this ensures that policies of a specific app are applied when the app cannot be determined automatically.
```python
from inuits_policy_based_auth.authentication.policies.token_based_policies.authlib_flask_oauth2_policy import (
    AuthlibFlaskOauth2Policy,
)
from inuits_policy_based_auth.authorization.policies.super_admin_policy import (
    SuperAdminPolicy,
)


policy_factory.register_authentication_policy("apps.[app_name]", AuthlibFlaskOauth2Policy(...))
policy_factory.register_authorization_policy("apps.[app_name]", SuperAdminPolicy())
policy_factory.set_fallback_key_for_policy_mapping("apps.[app_name]")
```
However, it is recommended to load policies dynamically as this will allow you to make use of the full potential of this package.

### Dynamically loading policies
You can write a loader which loads policies dynamically based on a configuration file.

Example configuration file:
```json
{
  "[app_name_1]": {
    "policies": {
      "authentication": [
        "token_based_policies.authlib_flask_oauth2_policy"
      ],
      "authorization": [
        "super_admin_policy",
        "scope_based_policy"
      ]
    }
  },
  "[app_name_2]": {
    "policies": {
      "authentication": [
        "token_based_policies.authlib_flask_oauth2_policy"
      ],
      "authorization": [
        "super_admin_policy",
        "open_data_policy"
      ]
    }
  }
}
```

Example policy_loader.py:
```python
import json
import os

from importlib import import_module
from inuits_policy_based_auth import PolicyFactory
from inuits_policy_based_auth.exceptions import (
    PolicyFactoryException,
)
from logging import Logger


def load_policies(policy_factory: PolicyFactory, logger: Logger):
    apps = {}

    configuration_file_name = os.getenv("[CONFIGURATION_FILE_NAME]") or ""
    with open(configuration_file_name) as configuration_file:
        apps = json.load(configuration_file)

    for app in apps:
        try:
            auth_type = "authentication"
            for policy_module_name in apps[app]["policies"].get(auth_type):
                policy = __get_class(app, auth_type, policy_module_name)
                policy = __instantiate_authentication_policy(
                    policy_module_name, policy, logger
                )
                policy_factory.register_authentication_policy(
                    f"apps.{app}", policy
                )

            auth_type = "authorization"
            for policy_module_name in apps[app]["policies"].get(auth_type):
                policy = __get_class(app, auth_type, policy_module_name)
                policy_factory.register_authorization_policy(
                    f"apps.{app}", policy()
                )
        except Exception as error:
            raise PolicyFactoryException(
                f"Policy factory was not configured correctly: {str(error)}"
            ).with_traceback(error.__traceback__)

    policy_factory.set_fallback_key_for_policy_mapping("apps.[app_name]")


def __get_class(app, auth_type, policy_module_name):
    module = None

    try:
        module = import_module(f"apps.{app}.policies.{auth_type}.{policy_module_name}")
    except:
        module = import_module(
            f"inuits_policy_based_auth.{auth_type}.policies.{policy_module_name}"
        )

    policy_class_name = module.__name__.split(".")[-1].title().replace("_", "")
    policy = getattr(module, policy_class_name)
    return policy


def __instantiate_authentication_policy(policy_module_name, policy, logger: Logger):
    if policy_module_name == "token_based_policies.authlib_flask_oauth2_policy":
        return policy(
            logger,
            os.getenv("STATIC_ISSUER", False),
            os.getenv("STATIC_PUBLIC_KEY", False),
            os.getenv("REALMS", "").split(","),
            os.getenv("ROLE_SCOPE_MAPPING", os.getenv("TEST_API_SCOPES")),
            os.getenv("REMOTE_TOKEN_VALIDATION", False) in ["True", "true", True],
            os.getenv("REMOTE_PUBLIC_KEY", False),
        )

    return policy()
```

Now you can import the loader in app.py and pass ```policy_factory``` as an argument to it.
```python
from apps.policy_loader import load_policies
from logging import Logger


load_policies(policy_factory, Logger(""))
```
As you can see in these examples, dynamically loading policies will allow you to add new policies and override existing ones, which makes this package highly customizable and generic.

### Custom policies
Continuing from the examples above, you can make a custom authorization policy by creating a folder ```policies``` within a specific app. Here you should create the folders ```authentication``` and ```authorization``` that will contain custom policies which you can add to your configuration. In this case we name our new policy the same as an existing one, which will override it. Each authentication policy must inherit from BaseAuthenticationPolicy and implement the abstract method ```authenticate```, while each authorization policy must inherit from BaseAuthorizationPolicy and implement the abstract method ```authorize```.

Example folder structure:
```
api
├── apps
│  ├── [app_name]
│  │  ├── policies
│  │  │  ├── authentication
│  │  │  └── authorization
│  │  │     └── open_data_policy.py
│  │  ├── ...
...
│  ├── configuration.json
│  └── policy_loader.py
...
└── app.py
```

Example custom open_data_policy.py:
```python
from inuits_policy_based_auth import BaseAuthorizationPolicy


class OpenDataPolicy(BaseAuthorizationPolicy):
    def authorize(self, policy_context, user_context, request_context):
        request = request_context.http_request
        if request.method == "GET" and user_context.tenant == "inuits":
            policy_context.access_verdict = True

        return policy_context
```

## Usage
If everything is set up correctly, you can use the ```apply_policies``` decorator as follows:
```python
from app import policy_factory
from flask import request
from inuits_policy_based_auth import RequestContext


class Entity():
    @policy_factory.apply_policies(
        RequestContext(request, ["[scope_1]", "[scope_2]"])
    )
    def get(self):
        ...
```

You can also use the ```authenticate``` decorator to only apply authentication policies:
```python
from app import policy_factory


class Entity():
    @policy_factory.authenticate()
    def get(self):
        ...
```

## Contributing
Do not hesitate to open issues and create pull requests.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "inuits-policy-based-auth",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "",
    "author": "Inuits",
    "author_email": "developers@inuits.eu",
    "download_url": "https://files.pythonhosted.org/packages/25/3f/8b34796a89ec0005ff2a7e12479438b837355219c27e49ff8b8f5fae41d4/inuits_policy_based_auth-3.0.0.tar.gz",
    "platform": null,
    "description": "# inuits_policy_based_auth\ninuits_policy_based_auth is a python package for securing API endpoints based on policies.\n\n## Installation\nInstall inuits_policy_based_auth as follows:\n```\npip install inuits-policy-based-auth\n```\n\n## Getting Started\nInstantiate the PolicyFactory in you app, for example in app.py (all examples given are based on a Python Flask app).\n```python\nfrom inuits_policy_based_auth import PolicyFactory\n\n\npolicy_factory = PolicyFactory()\n```\n### Manually loading policies\nImporting and registering policies can be done manually. Don't forget to set a fallback key, this ensures that policies of a specific app are applied when the app cannot be determined automatically.\n```python\nfrom inuits_policy_based_auth.authentication.policies.token_based_policies.authlib_flask_oauth2_policy import (\n    AuthlibFlaskOauth2Policy,\n)\nfrom inuits_policy_based_auth.authorization.policies.super_admin_policy import (\n    SuperAdminPolicy,\n)\n\n\npolicy_factory.register_authentication_policy(\"apps.[app_name]\", AuthlibFlaskOauth2Policy(...))\npolicy_factory.register_authorization_policy(\"apps.[app_name]\", SuperAdminPolicy())\npolicy_factory.set_fallback_key_for_policy_mapping(\"apps.[app_name]\")\n```\nHowever, it is recommended to load policies dynamically as this will allow you to make use of the full potential of this package.\n\n### Dynamically loading policies\nYou can write a loader which loads policies dynamically based on a configuration file.\n\nExample configuration file:\n```json\n{\n  \"[app_name_1]\": {\n    \"policies\": {\n      \"authentication\": [\n        \"token_based_policies.authlib_flask_oauth2_policy\"\n      ],\n      \"authorization\": [\n        \"super_admin_policy\",\n        \"scope_based_policy\"\n      ]\n    }\n  },\n  \"[app_name_2]\": {\n    \"policies\": {\n      \"authentication\": [\n        \"token_based_policies.authlib_flask_oauth2_policy\"\n      ],\n      \"authorization\": [\n        \"super_admin_policy\",\n        \"open_data_policy\"\n      ]\n    }\n  }\n}\n```\n\nExample policy_loader.py:\n```python\nimport json\nimport os\n\nfrom importlib import import_module\nfrom inuits_policy_based_auth import PolicyFactory\nfrom inuits_policy_based_auth.exceptions import (\n    PolicyFactoryException,\n)\nfrom logging import Logger\n\n\ndef load_policies(policy_factory: PolicyFactory, logger: Logger):\n    apps = {}\n\n    configuration_file_name = os.getenv(\"[CONFIGURATION_FILE_NAME]\") or \"\"\n    with open(configuration_file_name) as configuration_file:\n        apps = json.load(configuration_file)\n\n    for app in apps:\n        try:\n            auth_type = \"authentication\"\n            for policy_module_name in apps[app][\"policies\"].get(auth_type):\n                policy = __get_class(app, auth_type, policy_module_name)\n                policy = __instantiate_authentication_policy(\n                    policy_module_name, policy, logger\n                )\n                policy_factory.register_authentication_policy(\n                    f\"apps.{app}\", policy\n                )\n\n            auth_type = \"authorization\"\n            for policy_module_name in apps[app][\"policies\"].get(auth_type):\n                policy = __get_class(app, auth_type, policy_module_name)\n                policy_factory.register_authorization_policy(\n                    f\"apps.{app}\", policy()\n                )\n        except Exception as error:\n            raise PolicyFactoryException(\n                f\"Policy factory was not configured correctly: {str(error)}\"\n            ).with_traceback(error.__traceback__)\n\n    policy_factory.set_fallback_key_for_policy_mapping(\"apps.[app_name]\")\n\n\ndef __get_class(app, auth_type, policy_module_name):\n    module = None\n\n    try:\n        module = import_module(f\"apps.{app}.policies.{auth_type}.{policy_module_name}\")\n    except:\n        module = import_module(\n            f\"inuits_policy_based_auth.{auth_type}.policies.{policy_module_name}\"\n        )\n\n    policy_class_name = module.__name__.split(\".\")[-1].title().replace(\"_\", \"\")\n    policy = getattr(module, policy_class_name)\n    return policy\n\n\ndef __instantiate_authentication_policy(policy_module_name, policy, logger: Logger):\n    if policy_module_name == \"token_based_policies.authlib_flask_oauth2_policy\":\n        return policy(\n            logger,\n            os.getenv(\"STATIC_ISSUER\", False),\n            os.getenv(\"STATIC_PUBLIC_KEY\", False),\n            os.getenv(\"REALMS\", \"\").split(\",\"),\n            os.getenv(\"ROLE_SCOPE_MAPPING\", os.getenv(\"TEST_API_SCOPES\")),\n            os.getenv(\"REMOTE_TOKEN_VALIDATION\", False) in [\"True\", \"true\", True],\n            os.getenv(\"REMOTE_PUBLIC_KEY\", False),\n        )\n\n    return policy()\n```\n\nNow you can import the loader in app.py and pass ```policy_factory``` as an argument to it.\n```python\nfrom apps.policy_loader import load_policies\nfrom logging import Logger\n\n\nload_policies(policy_factory, Logger(\"\"))\n```\nAs you can see in these examples, dynamically loading policies will allow you to add new policies and override existing ones, which makes this package highly customizable and generic.\n\n### Custom policies\nContinuing from the examples above, you can make a custom authorization policy by creating a folder ```policies``` within a specific app. Here you should create the folders ```authentication``` and ```authorization``` that will contain custom policies which you can add to your configuration. In this case we name our new policy the same as an existing one, which will override it. Each authentication policy must inherit from BaseAuthenticationPolicy and implement the abstract method ```authenticate```, while each authorization policy must inherit from BaseAuthorizationPolicy and implement the abstract method ```authorize```.\n\nExample folder structure:\n```\napi\n\u251c\u2500\u2500 apps\n\u2502  \u251c\u2500\u2500 [app_name]\n\u2502  \u2502  \u251c\u2500\u2500 policies\n\u2502  \u2502  \u2502  \u251c\u2500\u2500 authentication\n\u2502  \u2502  \u2502  \u2514\u2500\u2500 authorization\n\u2502  \u2502  \u2502     \u2514\u2500\u2500 open_data_policy.py\n\u2502  \u2502  \u251c\u2500\u2500 ...\n...\n\u2502  \u251c\u2500\u2500 configuration.json\n\u2502  \u2514\u2500\u2500 policy_loader.py\n...\n\u2514\u2500\u2500 app.py\n```\n\nExample custom open_data_policy.py:\n```python\nfrom inuits_policy_based_auth import BaseAuthorizationPolicy\n\n\nclass OpenDataPolicy(BaseAuthorizationPolicy):\n    def authorize(self, policy_context, user_context, request_context):\n        request = request_context.http_request\n        if request.method == \"GET\" and user_context.tenant == \"inuits\":\n            policy_context.access_verdict = True\n\n        return policy_context\n```\n\n## Usage\nIf everything is set up correctly, you can use the ```apply_policies``` decorator as follows:\n```python\nfrom app import policy_factory\nfrom flask import request\nfrom inuits_policy_based_auth import RequestContext\n\n\nclass Entity():\n    @policy_factory.apply_policies(\n        RequestContext(request, [\"[scope_1]\", \"[scope_2]\"])\n    )\n    def get(self):\n        ...\n```\n\nYou can also use the ```authenticate``` decorator to only apply authentication policies:\n```python\nfrom app import policy_factory\n\n\nclass Entity():\n    @policy_factory.authenticate()\n    def get(self):\n        ...\n```\n\n## Contributing\nDo not hesitate to open issues and create pull requests.\n",
    "bugtrack_url": null,
    "license": "GPLv2",
    "summary": "Module for securing API endpoints based on policies.",
    "version": "3.0.0",
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4f714395a5cc59473b3e38c0a4bcd9053a6cf10a305a38fed326351ec87713bc",
                "md5": "5863a95784127bd44b35e15b96f64666",
                "sha256": "baaa5d891c1ba3351cc7ebe5316a9cb89ac3252bb2b695963a8acd26b1edd952"
            },
            "downloads": -1,
            "filename": "inuits_policy_based_auth-3.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5863a95784127bd44b35e15b96f64666",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 25491,
            "upload_time": "2023-04-17T16:34:15",
            "upload_time_iso_8601": "2023-04-17T16:34:15.586304Z",
            "url": "https://files.pythonhosted.org/packages/4f/71/4395a5cc59473b3e38c0a4bcd9053a6cf10a305a38fed326351ec87713bc/inuits_policy_based_auth-3.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "253f8b34796a89ec0005ff2a7e12479438b837355219c27e49ff8b8f5fae41d4",
                "md5": "94b8a339f5275dcb651fd7740e31e61a",
                "sha256": "9d6cd31943615cd65470b0fc5c24aec981e2baf5a766130d9a6096c66a3b8483"
            },
            "downloads": -1,
            "filename": "inuits_policy_based_auth-3.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "94b8a339f5275dcb651fd7740e31e61a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 28551,
            "upload_time": "2023-04-17T16:34:18",
            "upload_time_iso_8601": "2023-04-17T16:34:18.529373Z",
            "url": "https://files.pythonhosted.org/packages/25/3f/8b34796a89ec0005ff2a7e12479438b837355219c27e49ff8b8f5fae41d4/inuits_policy_based_auth-3.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-04-17 16:34:18",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "inuits-policy-based-auth"
}
        
Elapsed time: 0.07954s