c5


Namec5 JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryThe Simplest Attribute Based Access Control Python Library
upload_time2024-10-20 23:22:14
maintainerNone
docs_urlNone
authorRaymond
requires_python<4.0,>=3.7
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Contracts, Policies
Pydentity is an authorization and access control library built to provide simple yet robust ABAC paradigms. (**NOTE: pydentity is not a strict ABAC system**) - it is simpler but without
sacrificing the power of ABAC.


## Components
Pydentity works on a &copy; Micro `C5 Paradigm` which translates to **`Control Content from Contact via Contracts Contextually`**. Sounds intimidating but it is really simple once broken down.


Every information system has:

- **Content**: this is the data you might want to protect i.e. data stored in the system or system(s)

- **Contacts**: Entities that want to come in contact with the data (owner of the data, someone granted access by owner, hacker, application) etc.

- **Context**: Information within the information system or that the information system has access to but exists outside of the **Content** or
protected data i.e. dates or times when no one is allowed to update content, ip address of a contact etc.

- **Contracts**: What is a legal or illegal operation when **Content**, **Contact**, **Context** is combined together

- **Controls**: The system, processes, tools etc. that deny or allows operations _to_ and _on_ content

So how does this all relate to C5 - From the definition it means:
You (your application, company) wants to **control** (allow, deny access _to_ or limit actions _on_) **content** (data, information) from **contact** (application, hacker, human)
via **contracts** (combinatory rules) **contextually** (which might include the use of data not stored within the content being controlled).


## Contracts
The first thing to understand is _Contracts_. Contracts are how you specify the relationships betweem **_Contacts_** and **_Content_**. How do we create Contracts? Let's look at some code

##### Code Snippet: 1a
```py
from pydentity import Contract

contract = Contract()
```


Simple enough, but then, how do you tell a contract what actions are possible, and how to identify **Content** to be **Control**led?

##### Code Snippet: 1b
```py
...  # code from above

contract.on('delete').to('/customers/:id')
```
To specify actions use the `.on(*actions)` method of the contracts object. Actions can be called whatever you want i.e. `.on('foo')` - for web applications `HTTP` verbs
can be an intuitive option or popular candidate.

It is also important to note that all **contract** object method calls are order agnostic, this means you called have also called the `.to('/customers/:id')` method before
the `.on('deletes')` method and still get the same output.

This means the code snippet in snippet `1b` above is equivalent to code in snippet `1c` below.

##### Code Snippet: 1c
```py
contract.to('/customers/:id').on('delete') 
```


### Contract Rules &amp; Conditions
Great, now we know how to build a contract and tell it what actions and **Content** `.to('a-unique-id-for-content')` it identifies. But a contract is useless if we have no way of specifying the
actual `content`, `contact`, and `context` data for which the contract is valid.


#### Controlling Access Based On Content
If you want to control access to content based on the data in the content itself - you tell your contract this by using the `.content(conditions: dict)` method
of the `contract` object. It is important to note that by default **`pydentity denies all access unless a contract is found`**.

```py
from pydentity import Contract


# pydentity by default denies all access where there is no contract
# so to delete unlocked orders - this contract is needed
c = Contract()
c.on('delete').to('orders').content({
    'unlocked': True
})


# this will allow any content at the address `/orders/:id` with category
# `Perishables` to be deleted
d = Contract()
d.on('delete').to('/orders/:id').contact({
    'category': 'Perishables'
})
```
We are going to cover `Contact`, and `Context` blocks in later sections, but before we do it is important to explain how their configurations dictionary works. Dictionaries, hashmaps,
associative arrays (different names for the same thing) have a common format - `a key` maps to `a value`.
Pydentity *conditions* i.e. `.contact(**conditions)`, `.content(**conditions)`, `.context(**conditions)` are dictionaries that are passed into the *content*, *contact*, and *context* `pydentity.Contract`
methods.

By default `condition keys` i.e. dict keys are used for specifying what fields in the block(s) - (content, contact, context) to target, and the `values` provide indication of what the value of those fields
should be i.e. `constant values` expected to be seen for given contract conditions.

For all condition dicts across all blocks i.e. `content, context, contact` - the default
combination logic is `AND` i.e.

```json
{"key": 0, "key2": 1}
```

means where key = 0 `AND` key2 = 1

and **`is the same as`**

```json
{"&key": 0, "&key2": 1}
```

this is because `&` as the default symbol for `condition keys`, in addition, `AND` is the default operation for condition dictionaries when no symbol is explicitly provided.

To use `OR` logic instead of `AND`, use the **OR** symbol `?` as the first character of your conditions dict i.e.

```py
from pydentity import Contract
from pydentity.conditions import GT  # other options -> GTE, LT, BETWEEN, IN, NIN etc


# this contract will allow
#   any contact
# to retrieve
#   any content
# where
#   name = 'Jon' OR age > 18
Contract().on('gets').to('/customers').content({
    '?name': 'Jon',
    '?age': GT(18)
})
```

```py
c = Contract()

# this contract means give everyone access when orders content is public AND unlocked
c.on('gets').to('/orders').content({
    'public': True,
    'unlocked': True
})

# this is the same as above but using explicit symbol notation
c.on('gets').to('/orders').content({
    '&public': True,
    '&unlocked': True
})


# this contract means give everyone access when orders content is public OR unlocked
c.on('gets').to('/orders').content({
    '?public': True,
    '?unlocked': True
})
```
To specify `OR` combinations the keys must use the explicit symbol notation with the symbol for `OR` which is a `?`.

```py
c = Contract()

# this contract means allow Contact to the content
# at address `/products/:id` when it is public OR unlocked
c.on('updates').to('/products/:id').content({
    '?public': True,
    '?unlocked': True
})
```

```py
from pydentity import Controls

... # code from previous block here

controls = Controls()
controls.add(c, d)
```
These contracts are loaded in memory and for test use cases that is fine, but to make the most of pydentity you might want to connect a storage engine
like a database for contracts to be saved and retrieved easily.

Currently only redis, postgres, sql server, oracle db, and mysql is supported as a storage engine.

```py
from pydentity import Contract, Controls

controls = Controls(engine='postgres', dsn=...)
contract = Contract()

contract.on('patches').to('/customers/:id').context({
    'cidr': '10.0.0.0/24'
})
# contracts above are added in-memory but not saved, to persist contracts?
await controls.save()

# for synchronous use cases use save sync
controls.saves()
```

```

# now we can provide some context, content, and contact rules/conditions we
# want to combine with the possible actions specified above

c.context({})  # let's use an empty dict for now - this will allow everyone but it's good enough for now
c.content({})  # empty dict for now
c.contact({})  # empty dict for now


# finally let's add this contract to our controls for saving so it can be used later
# await protocol.add_contract(c) also possible
protocol.add_contract_sync(c)
```


```py
# this will allow any contact with first_name of Tersoo to delete an order
d = Contract()
d.on('delete').to('orders/:id').contact({
    'first_name': 'Tersoo'
})
```


#### Contact
This is recorded personal identifying information that can be used to identify a persona (app, human etc).



## Crash Course
Pydentity is all about **identifying** _contacts_, protecting _content_, they can have access _to_, as well as the _context_ for that access to be valid. There are a few things to note about Pydentity namely:

- Context: 
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "c5",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.7",
    "maintainer_email": null,
    "keywords": null,
    "author": "Raymond",
    "author_email": "ortserga@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/d4/5d/529e2ef634adfb2fcfdf228d7bac0a4bd3897598fa05bf601e1088b37021/c5-0.1.0.tar.gz",
    "platform": null,
    "description": "# Contracts, Policies\nPydentity is an authorization and access control library built to provide simple yet robust ABAC paradigms. (**NOTE: pydentity is not a strict ABAC system**) - it is simpler but without\nsacrificing the power of ABAC.\n\n\n## Components\nPydentity works on a &copy; Micro `C5 Paradigm` which translates to **`Control Content from Contact via Contracts Contextually`**. Sounds intimidating but it is really simple once broken down.\n\n\nEvery information system has:\n\n- **Content**: this is the data you might want to protect i.e. data stored in the system or system(s)\n\n- **Contacts**: Entities that want to come in contact with the data (owner of the data, someone granted access by owner, hacker, application) etc.\n\n- **Context**: Information within the information system or that the information system has access to but exists outside of the **Content** or\nprotected data i.e. dates or times when no one is allowed to update content, ip address of a contact etc.\n\n- **Contracts**: What is a legal or illegal operation when **Content**, **Contact**, **Context** is combined together\n\n- **Controls**: The system, processes, tools etc. that deny or allows operations _to_ and _on_ content\n\nSo how does this all relate to C5 - From the definition it means:\nYou (your application, company) wants to **control** (allow, deny access _to_ or limit actions _on_) **content** (data, information) from **contact** (application, hacker, human)\nvia **contracts** (combinatory rules) **contextually** (which might include the use of data not stored within the content being controlled).\n\n\n## Contracts\nThe first thing to understand is _Contracts_. Contracts are how you specify the relationships betweem **_Contacts_** and **_Content_**. How do we create Contracts? Let's look at some code\n\n##### Code Snippet: 1a\n```py\nfrom pydentity import Contract\n\ncontract = Contract()\n```\n\n\nSimple enough, but then, how do you tell a contract what actions are possible, and how to identify **Content** to be **Control**led?\n\n##### Code Snippet: 1b\n```py\n...  # code from above\n\ncontract.on('delete').to('/customers/:id')\n```\nTo specify actions use the `.on(*actions)` method of the contracts object. Actions can be called whatever you want i.e. `.on('foo')` - for web applications `HTTP` verbs\ncan be an intuitive option or popular candidate.\n\nIt is also important to note that all **contract** object method calls are order agnostic, this means you called have also called the `.to('/customers/:id')` method before\nthe `.on('deletes')` method and still get the same output.\n\nThis means the code snippet in snippet `1b` above is equivalent to code in snippet `1c` below.\n\n##### Code Snippet: 1c\n```py\ncontract.to('/customers/:id').on('delete') \n```\n\n\n### Contract Rules &amp; Conditions\nGreat, now we know how to build a contract and tell it what actions and **Content** `.to('a-unique-id-for-content')` it identifies. But a contract is useless if we have no way of specifying the\nactual `content`, `contact`, and `context` data for which the contract is valid.\n\n\n#### Controlling Access Based On Content\nIf you want to control access to content based on the data in the content itself - you tell your contract this by using the `.content(conditions: dict)` method\nof the `contract` object. It is important to note that by default **`pydentity denies all access unless a contract is found`**.\n\n```py\nfrom pydentity import Contract\n\n\n# pydentity by default denies all access where there is no contract\n# so to delete unlocked orders - this contract is needed\nc = Contract()\nc.on('delete').to('orders').content({\n    'unlocked': True\n})\n\n\n# this will allow any content at the address `/orders/:id` with category\n# `Perishables` to be deleted\nd = Contract()\nd.on('delete').to('/orders/:id').contact({\n    'category': 'Perishables'\n})\n```\nWe are going to cover `Contact`, and `Context` blocks in later sections, but before we do it is important to explain how their configurations dictionary works. Dictionaries, hashmaps,\nassociative arrays (different names for the same thing) have a common format - `a key` maps to `a value`.\nPydentity *conditions* i.e. `.contact(**conditions)`, `.content(**conditions)`, `.context(**conditions)` are dictionaries that are passed into the *content*, *contact*, and *context* `pydentity.Contract`\nmethods.\n\nBy default `condition keys` i.e. dict keys are used for specifying what fields in the block(s) - (content, contact, context) to target, and the `values` provide indication of what the value of those fields\nshould be i.e. `constant values` expected to be seen for given contract conditions.\n\nFor all condition dicts across all blocks i.e. `content, context, contact` - the default\ncombination logic is `AND` i.e.\n\n```json\n{\"key\": 0, \"key2\": 1}\n```\n\nmeans where key = 0 `AND` key2 = 1\n\nand **`is the same as`**\n\n```json\n{\"&key\": 0, \"&key2\": 1}\n```\n\nthis is because `&` as the default symbol for `condition keys`, in addition, `AND` is the default operation for condition dictionaries when no symbol is explicitly provided.\n\nTo use `OR` logic instead of `AND`, use the **OR** symbol `?` as the first character of your conditions dict i.e.\n\n```py\nfrom pydentity import Contract\nfrom pydentity.conditions import GT  # other options -> GTE, LT, BETWEEN, IN, NIN etc\n\n\n# this contract will allow\n#   any contact\n# to retrieve\n#   any content\n# where\n#   name = 'Jon' OR age > 18\nContract().on('gets').to('/customers').content({\n    '?name': 'Jon',\n    '?age': GT(18)\n})\n```\n\n```py\nc = Contract()\n\n# this contract means give everyone access when orders content is public AND unlocked\nc.on('gets').to('/orders').content({\n    'public': True,\n    'unlocked': True\n})\n\n# this is the same as above but using explicit symbol notation\nc.on('gets').to('/orders').content({\n    '&public': True,\n    '&unlocked': True\n})\n\n\n# this contract means give everyone access when orders content is public OR unlocked\nc.on('gets').to('/orders').content({\n    '?public': True,\n    '?unlocked': True\n})\n```\nTo specify `OR` combinations the keys must use the explicit symbol notation with the symbol for `OR` which is a `?`.\n\n```py\nc = Contract()\n\n# this contract means allow Contact to the content\n# at address `/products/:id` when it is public OR unlocked\nc.on('updates').to('/products/:id').content({\n    '?public': True,\n    '?unlocked': True\n})\n```\n\n```py\nfrom pydentity import Controls\n\n... # code from previous block here\n\ncontrols = Controls()\ncontrols.add(c, d)\n```\nThese contracts are loaded in memory and for test use cases that is fine, but to make the most of pydentity you might want to connect a storage engine\nlike a database for contracts to be saved and retrieved easily.\n\nCurrently only redis, postgres, sql server, oracle db, and mysql is supported as a storage engine.\n\n```py\nfrom pydentity import Contract, Controls\n\ncontrols = Controls(engine='postgres', dsn=...)\ncontract = Contract()\n\ncontract.on('patches').to('/customers/:id').context({\n    'cidr': '10.0.0.0/24'\n})\n# contracts above are added in-memory but not saved, to persist contracts?\nawait controls.save()\n\n# for synchronous use cases use save sync\ncontrols.saves()\n```\n\n```\n\n# now we can provide some context, content, and contact rules/conditions we\n# want to combine with the possible actions specified above\n\nc.context({})  # let's use an empty dict for now - this will allow everyone but it's good enough for now\nc.content({})  # empty dict for now\nc.contact({})  # empty dict for now\n\n\n# finally let's add this contract to our controls for saving so it can be used later\n# await protocol.add_contract(c) also possible\nprotocol.add_contract_sync(c)\n```\n\n\n```py\n# this will allow any contact with first_name of Tersoo to delete an order\nd = Contract()\nd.on('delete').to('orders/:id').contact({\n    'first_name': 'Tersoo'\n})\n```\n\n\n#### Contact\nThis is recorded personal identifying information that can be used to identify a persona (app, human etc).\n\n\n\n## Crash Course\nPydentity is all about **identifying** _contacts_, protecting _content_, they can have access _to_, as well as the _context_ for that access to be valid. There are a few things to note about Pydentity namely:\n\n- Context: ",
    "bugtrack_url": null,
    "license": null,
    "summary": "The Simplest Attribute Based Access Control Python Library",
    "version": "0.1.0",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4fea491a8216807e867cab605bdc1ba186cdae507bd61187377e45766651311f",
                "md5": "2706dcdf0270165b8261f31cb660534e",
                "sha256": "8d89d67a4306c56027802d9f87665530a879fe3f8b9fe92f74ec40b26b9686d9"
            },
            "downloads": -1,
            "filename": "c5-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2706dcdf0270165b8261f31cb660534e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.7",
            "size": 9234,
            "upload_time": "2024-10-20T23:22:12",
            "upload_time_iso_8601": "2024-10-20T23:22:12.963721Z",
            "url": "https://files.pythonhosted.org/packages/4f/ea/491a8216807e867cab605bdc1ba186cdae507bd61187377e45766651311f/c5-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d45d529e2ef634adfb2fcfdf228d7bac0a4bd3897598fa05bf601e1088b37021",
                "md5": "c61b60147af4ac0108cc1ac0e4a42771",
                "sha256": "54c6cfda7e707ffc66b8b71280480c2ec2b6b181a3d0833164ffbeee4df77425"
            },
            "downloads": -1,
            "filename": "c5-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "c61b60147af4ac0108cc1ac0e4a42771",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.7",
            "size": 10529,
            "upload_time": "2024-10-20T23:22:14",
            "upload_time_iso_8601": "2024-10-20T23:22:14.918211Z",
            "url": "https://files.pythonhosted.org/packages/d4/5d/529e2ef634adfb2fcfdf228d7bac0a4bd3897598fa05bf601e1088b37021/c5-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-20 23:22:14",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "c5"
}
        
Elapsed time: 0.39877s