# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['targe']

package_data = \
{'': ['*']}

install_requires = \
['gid>=1.0.1,<2.0.0']

setup_kwargs = {
    'name': 'targe',
    'version': '0.1.0',
    'description': 'Powerful and flexible policy based authorization library.',
    'long_description': '# Targe\nPowerful and flexible policy based authorization library.\n\n\n## Installation\n\nWith pip:\n```\npip install targe\n```\n\nor with poetry\n\n```\npoetry add targe\n```\n\n## Quick start\n\n```python\nfrom targe import Auth, ActorProvider, Actor, Policy\nfrom targe.errors import AccessDeniedError\n\n\n# This will provide actor for auth mechanism\nclass MyActorProvider(ActorProvider):\n    def get_actor(self, actor_id: str) -> Actor:\n        return Actor(actor_id)\n\n\n# Initialise auth class\nauth = Auth(MyActorProvider())\n\n# Retrieves and authorizes actor by its id\nauth.authorize("actor_id")\n\n\n# `auth.guard` decorator assigns auth scope to a function and\n# protects it from non-authorized access\n@auth.guard(scope="protected")\ndef protect_this() -> None:\n    ...  # code that should be protected by auth\n\n\ntry:\n    protect_this()\nexcept AccessDeniedError:\n    ...  # this will fail as actor has no access to scope `protected`\n\nauth.actor.policies.append(Policy.allow("protected"))  # add `protected` scope to actor policies\nprotect_this()  # now this works\n```\n\n## Features\n\n### Customisable and flexible policy system\nPolicy system in targe is not limited to some keywords like `read`, `write`, `create`, etc. \nInstead it uses scopes, which can hold any value that makes sense in your application\'s domain \nlike `eat:salads`. To increase flexibility and control `targe` also allows for defining references\nthat can point to certain data in your application.\n\n### Minimal, close to 0 learning curve\nIf you already have some experience with other `acl` or `authorization` libraries there is \nalmost 0 learning curve. In order to start using the library you only need to learn 5 methods:\n- `Auth.guard`\n- `Auth.guard_after`\n- `Policy.allow`\n- `Policy.deny`\n- `ActorProvider.get_actor`\n\n### Built-in audit log\nEverytime guarded function is executed library creates a log entry. This log entries can be persisted\nand used later on to understand who, when, how and what has changed within your application.\n\n### Elegant and easy to use interface\nYou don\'t have to write complex `if` statements asserting whether user has given role, policy. \nAll of that happens automatically in one small `@guard` decorator, which can be attached to \nany function/method within your codebase and easily moved away if needed. \n\n# Usage\n\n## Execution flow\n\nThe following diagram is a high level representation of the execution flow.\n\n![execution flow](./docs/execution_flow.png)\n\nWhen function gets called, instance of `Auth` class is checking whether `actor` is accessible \n(this should happen when `Auth.init` is called). \nIf `actor` is not accessible because `Auth.init` was not called or because `AuthStore.get_user` returned `null` value, \n`UnauthorizedError` exception is raised. \n\nWhen `actor` is present, library will try to resolve `reference` (reference contains a value that points to a piece \nof information stored in your application), reference resolving will occur if expression passed in `ref` attribute \nin the `guard` decorator.\n\nEverytime function is being called, library automatically generates audit log, which later on might be used to \nunderstand how, by who and whether guarded data in your system has being changed and/or accessed. \n\nThe last step is execution of guarded function.\n\n## Actor\nActor represents or references to a user in your application. Other important characteristics are:\n- an actor aggregates permissions and roles\n- an actor encapsulates its state and may act upon its change  \n- actor knows whether is can access given scope\n- actor\'s id is referenced in audit log  \n- actor can be extended further to encapsulate your application logic \n\n### Instantiating actor\n\n```python\nfrom targe import Actor\n\nmy_actor = Actor("actor_id")\n```\n\n### Assigning policies\n\n```python\nfrom targe import Actor, Policy\n\nmy_actor = Actor("actor_id")\n\n# assign policies \nmy_actor.policies.append(Policy.allow("articles:update"))\n```\n\n### Assigning roles\n\n```python\nfrom targe import Actor, Policy, Role\n\nmy_actor = Actor("actor_id")\n\n# simple role\nuser_manager = Role("user_manager")\nuser_manager.policies.append(Policy.allow("user:*"))\n\n# assign role\nmy_actor.roles.append(user_manager)\n```\n\n### Providing actor to auth system\nBy default, auth system does not know who is your actor and what it can do. \nTo provide information about your actor, you have to implement `targe.ActorProvider` protocol, \nplease consider the following example:\n\n```python\nfrom targe import ActorProvider, Actor, Auth\n\n\nclass MyActorProvider(ActorProvider):\n    def get_actor(self, actor_id: str) -> Actor:\n        ...  # you can query your database or do other relevant task to factory your instance of `targe.Actor`\n        return Actor(actor_id)\n\n\n# now we have to just instantiate auth and pass instance of our ActorProvider implementation\nauth = Auth(MyActorProvider())\n\n# The following line will cause auth system to use `MyActorProvider.get_actor` method.\nauth.authorize("actor_id")\n```\n\n## Policies\n\nPolicy is an object representing logical rule describing how and what type of information\ncan be accessed in your application. \nOnce policies are created they can ba attached to a role, or a user to ensure fine-grained\naccess control.\n\nPolicies contain `scopes` and `references`. The first ones holds an information how data is \nbeing accessed within your application (`read`, `write`, `update`, `etc`), \nthe latter ones define a rule that might limit accessibility to a single entity \nor entire group of entities. \n\nThe following code snippet shows an example policy that might be used to allow user \nupdating articles in specified category `animals`.\n\n```python\nfrom targe import Policy\n\npolicy = Policy.allow(scope="articles:update", ref="articles:animals:*")\n```\n\nHaving policy above we could also specify an article with an id of `article_id` \nwithin `animals` category that should not be updated:\n\n```python\nfrom targe import Policy\n\npolicy = Policy.deny("articles:update", "articles:animals:article_id")\n```\n\n### Scopes\n\nScopes can be used to set logical boundaries in your application. These are the boundaries \nin which data is being accessed and/or manipulated. Scope names can contain `:` (namespace separator) \nto improve granularity e.g.: `article:meta:setKeywords`.\n\nDefining policy per scope can be repetitive task, consider the following example:\n\n```python\nfrom targe import Policy\n\nPolicy.allow("article:meta:setKeywords")\nPolicy.allow("article:meta:setVersion")\nPolicy.allow("article:meta:setCategory")\nPolicy.allow("article:meta:getKeywords")\nPolicy.allow("article:meta:getVersion")\nPolicy.allow("article:meta:getCategory")\n...\n```\n\n> Note: if no reference is provided by default everything is accessible within given scope.\n\nIn the scenarios like this, `targe` provides pattern matching mechanism, so the above can be simplified to:\n\n```python\nfrom targe import Policy\n\nPolicy.allow("article:meta:set*")\nPolicy.allow("article:meta:get*")\n```\n\n### References\n\nReferences can be used to identify and/or logically group your data. References are using similar \nmechanism to scopes, which means in policies definition you can take advantage of `:` (namespace separator)\nsame way like you do it in the scope definition. \n\nYou can define as many references as needed, as long as they do not collapse, e.g.:\nImagine you have two references expressions, first follows schema `users:{group}:{id}`, \nthe other one `users:{group}:{sub-group}:{id}`. \n\nLet\'s have a look how pattern matching will work in this scenario:\n```\nusers:{group}:{id}\n               +\n               |    When matching reference with pattern `users:group:*`, we can match both\n               |    all users within all {sub-groups} and all users within a {group},\n               |    so having these two references in our application can cause problems.\n               +\nusers:{group}:{sub-group}:{id}\n```\n\nDefining additional namespace inside your reference can solve the problem, it may follow \nthe schema `{resource_type}:{namespace}:{logical-group-n}:{logical-group-n+1}:{id}`:\n\n```\nusers:by_group:{group}:{id}\n        +\n        |   Because we have additonal namespace element which is unique (`by_group` in the first case and `by_subgroup`\n        |   in the second case), we can safely use both references together in our application.\n        +\nusers:by_subgroup:{group}:{sub-group}:{id}\n```\n\n> It is recommended to have one schema reference per resource type unless \n> your schema grows large (has many namespace elements).\n\n## Roles\n\nRole is a collection of policies with a unique name. Roles can be also \nused to build Role-based access control (RBAC), which is a simplified mechanism\nfor regulating access to part of your application based on the roles \nof individual actor.\n\nHere\'s an example Role that can be used to grand access to user management:\n\n```python\nfrom targe import Role, Policy\n\nrole = Role("user_manager")\n\n# You can also attach policies, it is not needed if you are planning to build\n# authorization system based on RBAC\nrole.policies.append(Policy.allow("user:create"))\nrole.policies.append(Policy.allow("user:update"))\nrole.policies.append(Policy.allow("user:delete"))\nrole.policies.append(Policy.allow("user:read"))\n```\n\n> Role names must follow [a-z][a-z0-9_-] pattern. Role name is also its identifier, \n> thus they should be unique across your application.\n\n## Guarding function\n\nProtecting function from unauthorized access is one of the main objectives of this library.\nWe can protect function in two styles:\n- acl based style\n- rbac style\n\nUse rbac style in scenarios where you have to just assert if actor has given role, use acl based style in other cases.\nACL based style is not only giving you more control over your resources but also enables audit log. \n\n### Guarding function - rbac style example\n\nTo protect function from unauthorized execution use `Auth.guard(rbac=[...])` decorator with `rbac` argument. The `rbac`\nargument accepts list of strings where each string is a role name that is required in to execute annotated function.\n\n> If more than one role is passed in the `rbac` argument, this means actor has to own all the required roles\n> to execute annotated function.\n\n```python\nfrom targe import ActorProvider, Actor, Auth\nfrom targe.errors import AccessDeniedError\n\nclass MyActorProvider(ActorProvider):\n    def get_actor(self, actor_id: str) -> Actor:\n        return Actor(actor_id)\n    \nauth = Auth(MyActorProvider())\n\nauth.authorize("actor_id")\n\n@auth.guard(rbac=["user_manager"])  # Here we use `Auth.guard` decorator to protect `create_user` function\ndef create_user() -> None:\n    ...\n\ntry:\n    create_user()\nexcept AccessDeniedError:\n    print("`create_user` is protected from unauthorized access.")\n```\n\n> Keep in mind you can still take advantage of audit log in rbac mode, \n> the only requirement is to provide `scope` argument in `Auth.guard` decorator.\n\n### Guarding function - acl style example\n\n```python\nfrom targe import ActorProvider, Actor, Auth\nfrom targe.errors import AccessDeniedError\n\nclass MyActorProvider(ActorProvider):\n    def get_actor(self, actor_id: str) -> Actor:\n        return Actor(actor_id)\n    \nauth = Auth(MyActorProvider())\n\nauth.authorize("actor_id")\n\n@auth.guard(scope="user:create") \ndef create_user() -> None:\n    ...\n\ntry:\n    create_user()\nexcept AccessDeniedError:\n    print("`create_user` is protected from unauthorized access.") \n```\n\n#### Implementing custom behaviour\n\n## Audit log\n\n### Persisting audit log\n',
    'author': 'Dawid Kraczkowski',
    'author_email': 'dawid.kraczkowski@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/kodemore/targe',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
