The Simplest Attribute Based Access Control Python Library
Project description
Contracts, Policies
Pidentity is an authorization and access control library built to provide simple yet robust ABAC paradigms. (NOTE: Pidentity is not a strict ABAC system) - it is simpler but without sacrificing the power of ABAC.
Components
pidentity works on a © 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
from pidentity 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 Controlled?
Code Snippet: 1b
... # 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
contract.to('/customers/:id').on('delete')
Contract Rules & 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 pidentity denies all access unless a contract is found
.
from pidentity import Contract
# pidentity 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
.
pidentity conditions i.e. .contact(**conditions)
, .content(**conditions)
, .context(**conditions)
are dictionaries that are passed into the content, contact, and context pidentity.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.
{"key": 0, "key2": 1}
means where key = 0 AND
key2 = 1
and is the same as
{"&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.
from pidentity import Contract
from pidentity.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)
})
# OR, AND and others are non greedy and top to bottom
# that means &age, &name, ?role, &department
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 ?
.
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
})
from pidentity 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 pidentity 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.
from pidentity 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)
# 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
pidentity 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 pidentity namely:
- Context:
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file pidentity-0.1.2.tar.gz
.
File metadata
- Download URL: pidentity-0.1.2.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.3 CPython/3.12.5 Darwin/23.6.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f1e3171d3915d2ec5c60dacd1a3ac38a85fe6b60133457560e61428272629a74 |
|
MD5 | 2d92d6adfd0a9bbe94bd427261d01725 |
|
BLAKE2b-256 | 503688fcf5e83e823b95cf2489f62960e52e18b92bf7f4e3f22ad4bdc49f85c8 |
File details
Details for the file pidentity-0.1.2-py3-none-any.whl
.
File metadata
- Download URL: pidentity-0.1.2-py3-none-any.whl
- Upload date:
- Size: 12.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.3 CPython/3.12.5 Darwin/23.6.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 16523358a2c6d8669a4ba3de78f31e13b591a88f84a4f651273f3426ec5ccb87 |
|
MD5 | acadfc45917ee96dae7af1580192316e |
|
BLAKE2b-256 | 9507d76fad304d18c863ee60402da40b8e58dfe22e9eee0e44d843ff013bab31 |