Permissions¶
Kinto-Core provides a mechanism to handle authorization on the stored objects.
This section gives details about the behaviour of resources in regards to permissions.
Resource¶
from kinto.core import resource
@resource.register()
class Toadstool(resource.Resource):
schema = MushroomSchema
With this resource class, Kinto-Core will register the endpoints with a specific route factory, that will take care of checking the appropriate permission for each action.
Method |
URL |
Comments |
|
---|---|---|---|
GET / HEAD |
/{resource} |
|
If not allowed by setting
|
POST |
/{resource} |
|
Allowed by setting
|
DELETE |
/{resource} |
|
If not allowed by setting
|
GET / HEAD |
/{resource}/{id} |
|
If not allowed by setting
|
PUT |
/{resource}/{id} |
|
Allowed by setting
|
PATCH |
/{resource}/{id} |
|
If not allowed by setting
|
DELETE |
/{resource}/{id} |
|
If not allowed by setting
|
The objects permissions can be manipulated via the permissions
attribute in the
JSON payload, aside the data
attribute.
It allows to specify the list of principals allowed for each permission
,
as detailed in the API section.
Important
When defining permissions, there are two specific principals:
system.Authenticated
: any authenticated usersystem.Everyone
: any user
The write
permission is required to be able to modify the permissions
of an existing object.
When an object is created or modified, the current user is added to
list of principals for the write
permission on this object.
That means that a user is always able to replace or delete the records she created.
Manipulate permissions¶
One way of achieving dynamic permissions is to manipulate the permission backend manually.
For example, in some imaginary admin view:
def admin_view(request):
# Custom Pyramid view.
permission = request.registry.permission
# Give `create` permission to `user_id` in POST
some_user_id = request.POST['user_id']
permission_object_id = '/articles'
permission = 'create'
permission.add_principal_to_ace(permission_object_id,
permission,
some_user_id)
Or during application init (or scripts):
def main(global_config, **settings):
# ...
kinto.core.initialize(config, __version__)
# ...
some_user_id = 'ldap:alice@corp.com'
permission_object_id = '/articles'
permission = 'create'
config.registry.permission.add_principal_to_ace(permission_object_id,
permission,
some_user_id)
Since principals can be anything, it is also possible to use them to define groups:
def add_to_admins(request):
# Custom Pyramid view.
permission = request.registry.permission
some_user_id = request.POST['user_id']
group_name = 'group:admins'
permission.add_user_principal(some_user_id, group_name)
And then refer as group:admins
in the list of allowed principals.
Custom permission checking¶
The permissions verification in Kinto-Core is done with usual Pyramid authorization abstractions. Most notably using an implementation of a RootFactory in conjonction with an Authorization policy.
In order to completely override (or mimic) the defaults, a custom RootFactory and a custom Authorization policy can be plugged on the resource during registration.
from kinto.core import resource
class MyViewSet(resource.ViewSet):
def get_view_arguments(self, endpoint_type, resource_cls, method):
args = super().get_view_arguments(endpoint_type,
resource_cls,
method)
if method.lower() not in ('get', 'head'):
args['permission'] = 'publish'
return args
def get_service_arguments(self):
args = super().get_service_arguments()
args['factory'] = myapp.MyRootFactory
return args
@resource.register(viewset=MyViewSet())
class Resource(resource.Resource):
schema = BookmarkSchema
See more details about available customization in the viewset section.
A custom RootFactory and AuthorizationPolicy should implement the permission checking using Pyramid mecanisms.
For example, a simplistic example with the previous resource viewset:
from pyramid.interfaces import IAuthorizationPolicy
class MyRootFactory:
def __init__(self, request):
self.current_resource = None
service = request.current_service
if service and hasattr(service, 'resource'):
self.current_resource = service.resource
@implementer(IAuthorizationPolicy)
class AuthorizationPolicy:
def permits(self, context, principals, permission):
if context.current_resource == BlogArticle:
if permission == 'publish':
return 'group:publishers' in principals
return False
- class kinto.core.authorization.AuthorizationPolicy¶
Default authorization class, that leverages the permission backend for shareable resources.
- get_bound_permissions = None¶
Callable that takes an object id and a permission and returns a list of tuples (<object id>, <permission>). Useful when objects permission depend on others.
Backends¶
The ACLs are stored in a permission backend. Like for Storage and Cache, it is pluggable from configuration.
PostgreSQL¶
- class kinto.core.permission.postgresql.Permission(client, *args, **kwargs)¶
Permission backend using PostgreSQL.
Enable in configuration:
kinto.permission_backend = kinto.core.permission.postgresql
Database location URI can be customized:
kinto.permission_url = postgresql://user:pass@db.server.lan:5432/dbname
Alternatively, username and password could also rely on system user ident or even specified in
~/.pgpass
(see PostgreSQL documentation).Note
Some tables and indices are created when
kinto migrate
is run. This requires some privileges on the database, or some error will be raised.Alternatively, the schema can be initialized outside the python application, using the SQL file located in
kinto/core/permission/postgresql/schema.sql
. This allows to distinguish schema manipulation privileges from schema usage.A connection pool is enabled by default:
kinto.permission_pool_size = 10 kinto.permission_maxoverflow = 10 kinto.permission_max_backlog = -1 kinto.permission_pool_recycle = -1 kinto.permission_pool_timeout = 30 kinto.cache_poolclass = kinto.core.storage.postgresql.pool.QueuePoolWithMaxBacklog
The
max_backlog
limits the number of threads that can be in the queue waiting for a connection. Once this limit has been reached, any further attempts to acquire a connection will be rejected immediately, instead of locking up all threads by keeping them waiting in the queue.See dedicated section in SQLAlchemy documentation for default values and behaviour.
Note
Using a dedicated connection pool is still recommended to allow load balancing, replication or limit the number of connections used in a multi-process deployment.
- Noindex
Redis¶
See Kinto Redis driver plugin repository for more information.
API¶
Implementing a custom permission backend consists in implementating the following interface: