SAPL-Flask


NameSAPL-Flask JSON
Version 0.3.5 PyPI version JSON
download
home_page
SummaryLibrary to integrate SAPL into a Flask project
upload_time2023-09-10 23:33:40
maintainer
docs_urlNone
author
requires_python
licenseCopyright 2017-2022 Dominic Heutelbeck Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
keywords sapl abac security access control
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # SAPL Integration library for Flask

This library enables the usage of SAPL for a Flask application by providing decorators to enforce functions with SAPL functionality.
Basic knowledge about Attributes based access control(ABAC) is beneficial to understand the functionality of this library.

If you don't have any knowledge about ABAC, you should read the SAPL [documentation](https://sapl.io/documentation) before you continue reading.

## What is SAPL

SAPL is a further development of ABAC and can be described as Attribute-Stream-Based Access Control (ASBAC).
In contrary to ABAC, which is based on a request-response model and demands a new connection whenever a new decision is 
needed, SAPL works on a publish/subscribe model.
The client sends an Authorization Subscription to the Policy Decision Point(PDP) and receives a stream of Decisions, 
which can update the decision for the given Subscription for the client. 

The functionality of updating and enforcing newly received Decisions is done by the library in the background, so the 
developer can concentrate on writing the policies.

A complete documentation of SAPL can be found at [https://sapl.io](https://sapl.io), which also contains a playground to
write policies.


## how to install

The SAPL Flask integration library is released on PyPI and can be installed with the Package Manager pip.

To install SAPL_Flask you can use `pip install sapl_flask`. 

## Initialize the library

To initialize the library, the `init_sapl` function, which takes two arguments has to be called.

`def init_sapl(config: Config, subject_function: Callable[[], Any]):`

The first argument is an argument of the type Configuration, which works exactly like a dictionary.
The 2nd argument is a function, which returns Any, which will be the Subject of the Authorization Subscription.
What these functions are and how to write them is explained in the section [How to write subject function](#how-to-write-subject-function).

A simple project, which uses pre_enforce for a route, initializes SAPL_Flask and starts a Flask application would look like this:
```Python
import sapl_flask
from flask import Flask
from flask import session
from sapl_base.decorators import pre_enforce
app = Flask(__name__)

def subject_function():
  try:
    return session['access_token']
  except KeyError:
    return None

@app.route('/')
@pre_enforce
def hello_world():
  return "Hello World!"

if __name__ == "__main__":
    sapl_flask.init_sapl(app.config, subject_function)
    app.run()
```

## How to configure SAPL_Flask

The configuration is used to determine, how to connect to the PDP.
This PDP should be a SAPL server, for which a documented open source version is available on 
[GitHub](https://github.com/heutelbeck/sapl-policy-engine/tree/master/sapl-server-lt).

The easiest way to configure SAPL_Flask is to add a key `"POLICY_DECISION_POINT"` with according values to your Flask 
Configuration and use your Flask Configuration as the 1st argument of the init method.

The default configuration in JSON Format looks like this:
```json
{
  "POLICY_DECISION_POINT" : {
    "base_url": "http://localhost:8080/api/pdp/",
    "key": "YJidgyT2mfdkbmL",
    "secret": "Fa4zvYQdiwHZVXh",
    "dummy": false,
    "verify": false,
    "debug": false,
    "backoff_const_max_time": 1
  }
}
```
- base_url: The URL, where your PDP Server is located. This has to include the path `'/api/pdp/'` of the PDP Server. 
- key: Access to the API of the SAPL PDP Server requires "Basic Auth". The client key (username) can be set with this parameter.
  The default is the default implemented credentials of a [SAPL-Server-lt](https://github.com/heutelbeck/sapl-policy-engine/tree/master/sapl-server-lt)
- secret: The password which is used to get access to the API.
- dummy: Enables a dummy PDP, which is used instead of a remote PDP. This PDP always grants access and should never be used in production.
- verify: 
- debug: Enables debugging , which adds logging.
- backoff_const_max_time: When an error occurs, while requesting a Decision from the PDP SAPL_Flask does a retry. 
  This parameter determines, how many seconds the library should retry to connect, before it aborts and denies the access.

# Subject functions

For authentication, it has to be known who(subject) does the request. Flask does not have authentication built in 
from which the library could gather information about who did the request.
To determine who is requesting something you need to provide a function, which creates the subject for an Authorization 
Subscription. This function is called, whenever an Authorization Subscription is created. 
The value this function returns is used to create the subject for the Authorization Subscription.

## How to write a subject function

Subject function can be any function without parameter, which returns Any.
This function is called, when an Authorization Subscription is created.

An example for a subject function, where the subject is the access token of a session, if it is available.
```Python
from flask import session

def subject_function():
  try:
    return session['access_token']
  except KeyError:
    return None
```
If the function does return None, or an empty list, or dict, the subject will be set to "anonymous"

A Flask project, which uses SAPL_Flask, which is initialized with default configuration and this subject_function would be:

```Python
import sapl_flask
from flask import Flask
from flask import session
from sapl_base.decorators import pre_enforce
app = Flask(__name__)

def subject_function():
  try:
    return session['access_token']
  except KeyError:
    return None

@app.route('/')
@pre_enforce
def hello_world():
  return "Hello World!"

if __name__ == "__main__":
    sapl_flask.init_sapl(app.config, subject_function)
    app.run()
```

# How to use it

To implement SAPL into a Flask project, the functions, which shall be enforced by SAPL have to be decorated with SAPL decorators.
The decorator have to be the first decorator of a function. There are 3 possible decorators, which can be used for a function.

- `pre_enforce` when a function shall be enforced, before it is called.
- `post_enforce` when the function is already executed and the return_value is needed to determine if the requesting client has access to these data.
- `pre_and_post_enforce` when the function is enforced before and after it is called. This decorator can be used,
when the return value of the function is needed to finally decide if access can be granted, but the execution of the 
function is expensive and there are certain parameters of a request, which can already be used to deny access before the 
return value is known.

an example for a pre_enforced function would be:
```Python
from sapl_base.decorators import pre_enforce

@pre_enforce
def pre_enforced_function(*args,**kwargs):
    return_value = "Do something"
    return return_value
```

A decorated function will use the default implementation to create a subject,action and resources of an Authorization Subscription.
If no subject_functions are provided the subject is always "anonymous".

subject, action and resources are dictionarys, which are json dumped with a default JSON Converter.
These 3 dictionarys json formatted contain these values:

```json
{
  "subject": "is determined by the subject_functions",
  
  "action": {
    "function": {
      "function_name": "name of the function"
    },
    "request": {
      "path": "request.path",
      "method": "request.method",
      "endpoint": "request.endpoint",
      "route": "request.root_url",
      "blueprint": "request.blueprint"
    }
  },
  "resources": {
    "function": {
      "kwargs": "arguments with which the decorated function was called"
    },
    "request": {
      "GET" : "Arguments of the GET Request",
      "POST" : "Arguments of the POST Request"
    },
    "return_value": "Return value of the decorated function"
  }
}
```
To determine, who should have access to what values, Policies have to be written, which are used by the SAPL Server 
to evaluate the Decision for an Authorization Subscription.

Decisions can contain Obligations and Advices, which are in details explained in the section [Obligations and Advices].(#obligations-and-advices)
More Information about SAPL Server, Authorization Subscriptions, Obligations and Advices can be found in the 
[SAPL documentation](https://sapl.io/documentation)

SAPL_Flask does also support writing custom functions for the subject,action and/or resources of an Authorization 
Subscription, as well as providing values for these parameters, which replaces the default functions for these values.

## Providing arguments to a decorator

Instead of using the default implementation on how the subject,action and/or resources are created, it is possible to 
use values, or create your own functions to determine these values.
An example on how to use a constant as the value for the `action` argument would be
```Python
from sapl_base.decorators import pre_enforce

@pre_enforce(action="retrieve data")
def pre_enforced_function():
    return_value = "You are granted access to these data"
    return return_value
```
Whenever this function is called, it will be enforced before it is executed.
The value of the 'action' parameter of the Authorization Subscription will always be "retrieve data"

A more dynamic approach could get the type and path of the request, create a dictionary from these values and set this 
dictionary as action.
```Python
from flask import request
from sapl_base.decorators import pre_enforce

def create_action():
  return {"method": request.method,"path":request.path}

@pre_enforce(action=create_action)
def pre_enforced_function(*args,**kwargs):
    return_value = "Do something"
    return return_value
```


# Obligations and Advices

A Decision from the PDP can have Constraints attached to the Decision. There are two different kinds of Constraints,
Obligations and Advices. Obligations have to be handled, otherwise the Permission is Denied. Advices should be handled, 
but the Decision won't change when the Advices are not handled.

To handle these Constraints, this library offers an abstract class called `ConstraintHandlerProvider`, which can handle 
Constraints. The classes, which can handle the constraints are created by the developer and have to be registered to be available 
to the library, to check if given constraints can be handled.


## How to create ConstraintHandlerProvider

In order to create ConstraintHandlerProvider, 4 abstract ConstraintHandlerProvider are available to inherit from. These
abstract classes are: 

- `ErrorConstraintHandlerProvider`
- `OnDecisionConstraintHandlerProvider`
- `FunctionArgumentsConstraintHandlerProvider`
- `ResultConstraintHandlerProvider`

They all inherit from the base class `ConstraintHandlerProvider` and only differ in the types of the arguments their methods
take and return.

The Baseclass is defined like this:
```python
from abc import abstractmethod, ABC

class ConstraintHandlerProvider(ABC):
  
    @abstractmethod
    def priority(self) -> int:
        """
        ConstraintHandlerProvider are sorted by the value of the priority, when the ConstraintHandlerBundle is created

        :return: value by which ConstraintHandlerProvider are sorted
        """
        return 0

    @abstractmethod
    def is_responsible(self, constraint) -> bool:
        """
        Determine if this ConstraintHandler is responsible for the provided constraint

        :param constraint: A constraint, which can be an Obligation or an Advice, for which the
        ConstraintHandlerProvider checks if it is responsible to handle it.
        :return: Is this ConstraintHandlerProvider responsible to handle the provided constraint
        """
        pass

    @abstractmethod
    def handle(self, argument):
        """
        Abstractmethod, which needs to be implemented by a ConstraintHandlerProvider
        :param argument: The argument, which is provided to the ConstraintHandler, when it is called. This argument can 
        be an Exception, function, decision, or the result of the executed function.
        """
```

When a Decision contains a Constraint, the library checks all registered Constraint handler provider, if their 
`is_responsible` method evaluates to true for the given Constraint. 
The responsible Constraint handler provider are gathered and 
sorted by the value their method `priority` returns.
At the end, the `handle` methods of the sorted list of `ConstraintHandlerProvider`, which are responsible for a given 
Constraint are executed in the order of the list.

An example for a ConstraintHandlerProvider, which logs the received Decision when it contains a constraint which 
equals to "log decision" would be:
```python
from sapl_base.decision import Decision
from sapl_base.constraint_handling.constraint_handler_provider import OnDecisionConstraintHandlerProvider
import logging

class LogNewDecisionConstraintHandler(OnDecisionConstraintHandlerProvider):

    def handle(self, decision: Decision) -> None:
        logging.info(str(decision))

    def priority(self) -> int:
        return 0

    def is_responsible(self, constraint) -> bool:
        return True if constraint == "log decision" else False
```

## How to register ConstraintHandlerProvider

The class `ConstraintHandlerService` handles any Constraints and contains a singleton(constraint_handler_service), which 
is created when the file is loaded. All `ConstraintHandlerProvider` registered at this singleton are taken into account, 
when the Constraints of a Decision are checked, if they can be handled.

The ConstraintHandlerService has methods to register single ConstraintHandlerProvider, or lists of ConstraintHandlerProvider 
for each of the 4 types of ConstraintHandlerProvider.

The following code would initialize the library, register the previously created ConstraintHandlerProvider and launch 
the Flask app.

```Python
import sapl_flask
from sapl_base.constraint_handling.constraint_handler_service import constraint_handler_service
from sapl_base.decision import Decision
from sapl_base.decorators import pre_enforce
from sapl_base.constraint_handling.constraint_handler_provider import OnDecisionConstraintHandlerProvider
import logging
from flask import Flask,session


app = Flask(__name__)

class LogNewDecisionConstraintHandler(OnDecisionConstraintHandlerProvider):

    def handle(self, decision: Decision) -> None:
        logging.info(str(decision))

    def priority(self) -> int:
        return 0

    def is_responsible(self, constraint) -> bool:
        return True if constraint == "log decision" else False

def subject_function():
  try:
    return session['access_token']
  except KeyError:
    return None

@app.route('/')
@pre_enforce
def hello_world():
  return "Hello World!"
  
if __name__ == "__main__":
    sapl_flask.init_sapl(app.config, subject_function)
    constraint_handler_service.register_on_decision_constraint_handler_provider(LogNewDecisionConstraintHandler())
    app.run()
```

# How to migrate

If you have an existing project of Flask, you can add SAPL_Flask by installing and initializing the library.
The library can then be used by decorating functions and writing policies.


            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "SAPL-Flask",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "SAPL,ABAC,Security,Access control",
    "author": "",
    "author_email": "Dominic Heutelbeck <dominic@heutelbeck.com>",
    "download_url": "https://files.pythonhosted.org/packages/3a/0b/ea61143e85ae2a00545e0cbda8c0eb19c5c9918456f2b0983b26e80ad9e2/SAPL-Flask-0.3.5.tar.gz",
    "platform": null,
    "description": "# SAPL Integration library for Flask\n\nThis library enables the usage of SAPL for a Flask application by providing decorators to enforce functions with SAPL functionality.\nBasic knowledge about Attributes based access control(ABAC) is beneficial to understand the functionality of this library.\n\nIf you don't have any knowledge about ABAC, you should read the SAPL [documentation](https://sapl.io/documentation) before you continue reading.\n\n## What is SAPL\n\nSAPL is a further development of ABAC and can be described as Attribute-Stream-Based Access Control (ASBAC).\nIn contrary to ABAC, which is based on a request-response model and demands a new connection whenever a new decision is \nneeded, SAPL works on a publish/subscribe model.\nThe client sends an Authorization Subscription to the Policy Decision Point(PDP) and receives a stream of Decisions, \nwhich can update the decision for the given Subscription for the client. \n\nThe functionality of updating and enforcing newly received Decisions is done by the library in the background, so the \ndeveloper can concentrate on writing the policies.\n\nA complete documentation of SAPL can be found at [https://sapl.io](https://sapl.io), which also contains a playground to\nwrite policies.\n\n\n## how to install\n\nThe SAPL Flask integration library is released on PyPI and can be installed with the Package Manager pip.\n\nTo install SAPL_Flask you can use `pip install sapl_flask`. \n\n## Initialize the library\n\nTo initialize the library, the `init_sapl` function, which takes two arguments has to be called.\n\n`def init_sapl(config: Config, subject_function: Callable[[], Any]):`\n\nThe first argument is an argument of the type Configuration, which works exactly like a dictionary.\nThe 2nd argument is a function, which returns Any, which will be the Subject of the Authorization Subscription.\nWhat these functions are and how to write them is explained in the section [How to write subject function](#how-to-write-subject-function).\n\nA simple project, which uses pre_enforce for a route, initializes SAPL_Flask and starts a Flask application would look like this:\n```Python\nimport sapl_flask\nfrom flask import Flask\nfrom flask import session\nfrom sapl_base.decorators import pre_enforce\napp = Flask(__name__)\n\ndef subject_function():\n  try:\n    return session['access_token']\n  except KeyError:\n    return None\n\n@app.route('/')\n@pre_enforce\ndef hello_world():\n  return \"Hello World!\"\n\nif __name__ == \"__main__\":\n    sapl_flask.init_sapl(app.config, subject_function)\n    app.run()\n```\n\n## How to configure SAPL_Flask\n\nThe configuration is used to determine, how to connect to the PDP.\nThis PDP should be a SAPL server, for which a documented open source version is available on \n[GitHub](https://github.com/heutelbeck/sapl-policy-engine/tree/master/sapl-server-lt).\n\nThe easiest way to configure SAPL_Flask is to add a key `\"POLICY_DECISION_POINT\"` with according values to your Flask \nConfiguration and use your Flask Configuration as the 1st argument of the init method.\n\nThe default configuration in JSON Format looks like this:\n```json\n{\n  \"POLICY_DECISION_POINT\" : {\n    \"base_url\": \"http://localhost:8080/api/pdp/\",\n    \"key\": \"YJidgyT2mfdkbmL\",\n    \"secret\": \"Fa4zvYQdiwHZVXh\",\n    \"dummy\": false,\n    \"verify\": false,\n    \"debug\": false,\n    \"backoff_const_max_time\": 1\n  }\n}\n```\n- base_url: The URL, where your PDP Server is located. This has to include the path `'/api/pdp/'` of the PDP Server. \n- key: Access to the API of the SAPL PDP Server requires \"Basic Auth\". The client key (username) can be set with this parameter.\n  The default is the default implemented credentials of a [SAPL-Server-lt](https://github.com/heutelbeck/sapl-policy-engine/tree/master/sapl-server-lt)\n- secret: The password which is used to get access to the API.\n- dummy: Enables a dummy PDP, which is used instead of a remote PDP. This PDP always grants access and should never be used in production.\n- verify: \n- debug: Enables debugging , which adds logging.\n- backoff_const_max_time: When an error occurs, while requesting a Decision from the PDP SAPL_Flask does a retry. \n  This parameter determines, how many seconds the library should retry to connect, before it aborts and denies the access.\n\n# Subject functions\n\nFor authentication, it has to be known who(subject) does the request. Flask does not have authentication built in \nfrom which the library could gather information about who did the request.\nTo determine who is requesting something you need to provide a function, which creates the subject for an Authorization \nSubscription. This function is called, whenever an Authorization Subscription is created. \nThe value this function returns is used to create the subject for the Authorization Subscription.\n\n## How to write a subject function\n\nSubject function can be any function without parameter, which returns Any.\nThis function is called, when an Authorization Subscription is created.\n\nAn example for a subject function, where the subject is the access token of a session, if it is available.\n```Python\nfrom flask import session\n\ndef subject_function():\n  try:\n    return session['access_token']\n  except KeyError:\n    return None\n```\nIf the function does return None, or an empty list, or dict, the subject will be set to \"anonymous\"\n\nA Flask project, which uses SAPL_Flask, which is initialized with default configuration and this subject_function would be:\n\n```Python\nimport sapl_flask\nfrom flask import Flask\nfrom flask import session\nfrom sapl_base.decorators import pre_enforce\napp = Flask(__name__)\n\ndef subject_function():\n  try:\n    return session['access_token']\n  except KeyError:\n    return None\n\n@app.route('/')\n@pre_enforce\ndef hello_world():\n  return \"Hello World!\"\n\nif __name__ == \"__main__\":\n    sapl_flask.init_sapl(app.config, subject_function)\n    app.run()\n```\n\n# How to use it\n\nTo implement SAPL into a Flask project, the functions, which shall be enforced by SAPL have to be decorated with SAPL decorators.\nThe decorator have to be the first decorator of a function. There are 3 possible decorators, which can be used for a function.\n\n- `pre_enforce` when a function shall be enforced, before it is called.\n- `post_enforce` when the function is already executed and the return_value is needed to determine if the requesting client has access to these data.\n- `pre_and_post_enforce` when the function is enforced before and after it is called. This decorator can be used,\nwhen the return value of the function is needed to finally decide if access can be granted, but the execution of the \nfunction is expensive and there are certain parameters of a request, which can already be used to deny access before the \nreturn value is known.\n\nan example for a pre_enforced function would be:\n```Python\nfrom sapl_base.decorators import pre_enforce\n\n@pre_enforce\ndef pre_enforced_function(*args,**kwargs):\n    return_value = \"Do something\"\n    return return_value\n```\n\nA decorated function will use the default implementation to create a subject,action and resources of an Authorization Subscription.\nIf no subject_functions are provided the subject is always \"anonymous\".\n\nsubject, action and resources are dictionarys, which are json dumped with a default JSON Converter.\nThese 3 dictionarys json formatted contain these values:\n\n```json\n{\n  \"subject\": \"is determined by the subject_functions\",\n  \n  \"action\": {\n    \"function\": {\n      \"function_name\": \"name of the function\"\n    },\n    \"request\": {\n      \"path\": \"request.path\",\n      \"method\": \"request.method\",\n      \"endpoint\": \"request.endpoint\",\n      \"route\": \"request.root_url\",\n      \"blueprint\": \"request.blueprint\"\n    }\n  },\n  \"resources\": {\n    \"function\": {\n      \"kwargs\": \"arguments with which the decorated function was called\"\n    },\n    \"request\": {\n      \"GET\" : \"Arguments of the GET Request\",\n      \"POST\" : \"Arguments of the POST Request\"\n    },\n    \"return_value\": \"Return value of the decorated function\"\n  }\n}\n```\nTo determine, who should have access to what values, Policies have to be written, which are used by the SAPL Server \nto evaluate the Decision for an Authorization Subscription.\n\nDecisions can contain Obligations and Advices, which are in details explained in the section [Obligations and Advices].(#obligations-and-advices)\nMore Information about SAPL Server, Authorization Subscriptions, Obligations and Advices can be found in the \n[SAPL documentation](https://sapl.io/documentation)\n\nSAPL_Flask does also support writing custom functions for the subject,action and/or resources of an Authorization \nSubscription, as well as providing values for these parameters, which replaces the default functions for these values.\n\n## Providing arguments to a decorator\n\nInstead of using the default implementation on how the subject,action and/or resources are created, it is possible to \nuse values, or create your own functions to determine these values.\nAn example on how to use a constant as the value for the `action` argument would be\n```Python\nfrom sapl_base.decorators import pre_enforce\n\n@pre_enforce(action=\"retrieve data\")\ndef pre_enforced_function():\n    return_value = \"You are granted access to these data\"\n    return return_value\n```\nWhenever this function is called, it will be enforced before it is executed.\nThe value of the 'action' parameter of the Authorization Subscription will always be \"retrieve data\"\n\nA more dynamic approach could get the type and path of the request, create a dictionary from these values and set this \ndictionary as action.\n```Python\nfrom flask import request\nfrom sapl_base.decorators import pre_enforce\n\ndef create_action():\n  return {\"method\": request.method,\"path\":request.path}\n\n@pre_enforce(action=create_action)\ndef pre_enforced_function(*args,**kwargs):\n    return_value = \"Do something\"\n    return return_value\n```\n\n\n# Obligations and Advices\n\nA Decision from the PDP can have Constraints attached to the Decision. There are two different kinds of Constraints,\nObligations and Advices. Obligations have to be handled, otherwise the Permission is Denied. Advices should be handled, \nbut the Decision won't change when the Advices are not handled.\n\nTo handle these Constraints, this library offers an abstract class called `ConstraintHandlerProvider`, which can handle \nConstraints. The classes, which can handle the constraints are created by the developer and have to be registered to be available \nto the library, to check if given constraints can be handled.\n\n\n## How to create ConstraintHandlerProvider\n\nIn order to create ConstraintHandlerProvider, 4 abstract ConstraintHandlerProvider are available to inherit from. These\nabstract classes are: \n\n- `ErrorConstraintHandlerProvider`\n- `OnDecisionConstraintHandlerProvider`\n- `FunctionArgumentsConstraintHandlerProvider`\n- `ResultConstraintHandlerProvider`\n\nThey all inherit from the base class `ConstraintHandlerProvider` and only differ in the types of the arguments their methods\ntake and return.\n\nThe Baseclass is defined like this:\n```python\nfrom abc import abstractmethod, ABC\n\nclass ConstraintHandlerProvider(ABC):\n  \n    @abstractmethod\n    def priority(self) -> int:\n        \"\"\"\n        ConstraintHandlerProvider are sorted by the value of the priority, when the ConstraintHandlerBundle is created\n\n        :return: value by which ConstraintHandlerProvider are sorted\n        \"\"\"\n        return 0\n\n    @abstractmethod\n    def is_responsible(self, constraint) -> bool:\n        \"\"\"\n        Determine if this ConstraintHandler is responsible for the provided constraint\n\n        :param constraint: A constraint, which can be an Obligation or an Advice, for which the\n        ConstraintHandlerProvider checks if it is responsible to handle it.\n        :return: Is this ConstraintHandlerProvider responsible to handle the provided constraint\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def handle(self, argument):\n        \"\"\"\n        Abstractmethod, which needs to be implemented by a ConstraintHandlerProvider\n        :param argument: The argument, which is provided to the ConstraintHandler, when it is called. This argument can \n        be an Exception, function, decision, or the result of the executed function.\n        \"\"\"\n```\n\nWhen a Decision contains a Constraint, the library checks all registered Constraint handler provider, if their \n`is_responsible` method evaluates to true for the given Constraint. \nThe responsible Constraint handler provider are gathered and \nsorted by the value their method `priority` returns.\nAt the end, the `handle` methods of the sorted list of `ConstraintHandlerProvider`, which are responsible for a given \nConstraint are executed in the order of the list.\n\nAn example for a ConstraintHandlerProvider, which logs the received Decision when it contains a constraint which \nequals to \"log decision\" would be:\n```python\nfrom sapl_base.decision import Decision\nfrom sapl_base.constraint_handling.constraint_handler_provider import OnDecisionConstraintHandlerProvider\nimport logging\n\nclass LogNewDecisionConstraintHandler(OnDecisionConstraintHandlerProvider):\n\n    def handle(self, decision: Decision) -> None:\n        logging.info(str(decision))\n\n    def priority(self) -> int:\n        return 0\n\n    def is_responsible(self, constraint) -> bool:\n        return True if constraint == \"log decision\" else False\n```\n\n## How to register ConstraintHandlerProvider\n\nThe class `ConstraintHandlerService` handles any Constraints and contains a singleton(constraint_handler_service), which \nis created when the file is loaded. All `ConstraintHandlerProvider` registered at this singleton are taken into account, \nwhen the Constraints of a Decision are checked, if they can be handled.\n\nThe ConstraintHandlerService has methods to register single ConstraintHandlerProvider, or lists of ConstraintHandlerProvider \nfor each of the 4 types of ConstraintHandlerProvider.\n\nThe following code would initialize the library, register the previously created ConstraintHandlerProvider and launch \nthe Flask app.\n\n```Python\nimport sapl_flask\nfrom sapl_base.constraint_handling.constraint_handler_service import constraint_handler_service\nfrom sapl_base.decision import Decision\nfrom sapl_base.decorators import pre_enforce\nfrom sapl_base.constraint_handling.constraint_handler_provider import OnDecisionConstraintHandlerProvider\nimport logging\nfrom flask import Flask,session\n\n\napp = Flask(__name__)\n\nclass LogNewDecisionConstraintHandler(OnDecisionConstraintHandlerProvider):\n\n    def handle(self, decision: Decision) -> None:\n        logging.info(str(decision))\n\n    def priority(self) -> int:\n        return 0\n\n    def is_responsible(self, constraint) -> bool:\n        return True if constraint == \"log decision\" else False\n\ndef subject_function():\n  try:\n    return session['access_token']\n  except KeyError:\n    return None\n\n@app.route('/')\n@pre_enforce\ndef hello_world():\n  return \"Hello World!\"\n  \nif __name__ == \"__main__\":\n    sapl_flask.init_sapl(app.config, subject_function)\n    constraint_handler_service.register_on_decision_constraint_handler_provider(LogNewDecisionConstraintHandler())\n    app.run()\n```\n\n# How to migrate\n\nIf you have an existing project of Flask, you can add SAPL_Flask by installing and initializing the library.\nThe library can then be used by decorating functions and writing policies.\n\n",
    "bugtrack_url": null,
    "license": "Copyright 2017-2022 Dominic Heutelbeck  Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.",
    "summary": "Library to integrate SAPL into a Flask project",
    "version": "0.3.5",
    "project_urls": {
        "Documentation": "https://sapl.io/documentation",
        "Homepage": "https://sapl.io/",
        "Source": "https://github.com/heutelbeck/sapl-python/tree/master/sapl_base"
    },
    "split_keywords": [
        "sapl",
        "abac",
        "security",
        "access control"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c945cb0963cf6d69f607c5868c527ec111a3376dbce159fb0a77daca58329b57",
                "md5": "ec66ae664be8797ab37ca786e0345344",
                "sha256": "0a3289bdf0bbe3f969e3ce1552b6e4ff3733404d50b4085d7a16dbe29c79ef45"
            },
            "downloads": -1,
            "filename": "SAPL_Flask-0.3.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ec66ae664be8797ab37ca786e0345344",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 8821,
            "upload_time": "2023-09-10T23:33:38",
            "upload_time_iso_8601": "2023-09-10T23:33:38.842188Z",
            "url": "https://files.pythonhosted.org/packages/c9/45/cb0963cf6d69f607c5868c527ec111a3376dbce159fb0a77daca58329b57/SAPL_Flask-0.3.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3a0bea61143e85ae2a00545e0cbda8c0eb19c5c9918456f2b0983b26e80ad9e2",
                "md5": "923ea18e1e08e5b1cec720294dc87afb",
                "sha256": "0b0db89f8ddc9f037dc2a0d075fdc0316eb06b3b64e71480bf34b1ba2606ef61"
            },
            "downloads": -1,
            "filename": "SAPL-Flask-0.3.5.tar.gz",
            "has_sig": false,
            "md5_digest": "923ea18e1e08e5b1cec720294dc87afb",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 9152,
            "upload_time": "2023-09-10T23:33:40",
            "upload_time_iso_8601": "2023-09-10T23:33:40.648466Z",
            "url": "https://files.pythonhosted.org/packages/3a/0b/ea61143e85ae2a00545e0cbda8c0eb19c5c9918456f2b0983b26e80ad9e2/SAPL-Flask-0.3.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-10 23:33:40",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "heutelbeck",
    "github_project": "sapl-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "sapl-flask"
}
        
Elapsed time: 0.15384s