User resource
This is the simplest one, as presented in the resource section.
When using a kinto.core.resource.UserResource, every authenticated user
can manipulate and read their own records. There is no way to restrict this or
allow sharing of records.
| Method |
URL |
permission |
| GET / HEAD |
/{collection} |
Authenticated |
| POST |
/{collection} |
Authenticated |
| DELETE |
/{collection} |
Authenticated |
| GET / HEAD |
/{collection}/{id} |
Authenticated |
| PUT |
/{collection}/{id} |
Authenticated |
| PATCH |
/{collection}/{id} |
Authenticated |
| DELETE |
/{collection}/{id} |
Authenticated |
Note
When using only these resources, the permission backend remains unused.
Its configuration is not necessary.
Public BasicAuth
If Basic Auth authentication is enabled, private user resources can become semi-private or public
if the user:pass is publicly known and shared (for example public: is a valid user:pass combination).
That’s how most simple demos of Kinto are built by the way!
Shareable resource
To introduce more flexibility, the kinto.core.resource.ShareableResource
can be used instead.
from kinto.core import resource
@resource.register()
class Toadstool(resource.ShareableResource):
mapping = MushroomSchema()
With this alternative 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 |
permission |
Comments |
| GET / HEAD |
/{collection} |
read |
If not allowed by setting
kinto.{collection}_read_principals,
will return list of records where user
has read permission. |
| POST |
/{collection} |
create |
Allowed by setting
kinto.{collection}_create_principals |
| DELETE |
/{collection} |
write |
If not allowed by setting
kinto.{collection}_write_principals,
will delete the list of records where
user has write permission. |
| GET / HEAD |
/{collection}/{id} |
read |
If not allowed by setting
kinto.{collection}_read_principals,
will check record permissions |
| PUT |
/{collection}/{id} |
create if record
doesn’t exist,
write otherwise |
Allowed by setting
kinto.{collection}_create_principals,
or kinto.{collection}_create_principals
or existing record permissions |
| PATCH |
/{collection}/{id} |
write |
If not allowed by setting
kinto.{collection}_write_principals,
will check record permissions |
| DELETE |
/{collection}/{id} |
write |
If not allowed by setting
kinto.{collection}_write_principals,
will check record permissions |
The record 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 user
system.Everyone: any user
The write permission is required to be able to modify the permissions
of an existing record.
When a record 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.
Note
Don’t hesitate to submit a contribution to introduce a way to control the
current behaviour instead of always granting write on current user!
BasicAuth trickery
Like for user resources, if Basic Auth authentication is enabled, the predictable
user id can be used to define semi-private or public if the user:pass is
known and shared (for example public: is a valid user:pass combination).
For example, get the user id obtained in the hello root view with a
user:pass combination and use it in the permissions JSON payloads, or settings:
kinto.{collection}_read_principals = basicauth:631c2d625ee5726172cf67c6750de10a3e1a04bcd603bc9ad6d6b196fa8257a6
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 = 'basicauth:ut082jghnrgnjnj'
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(MyViewSet, self).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(MyViewSet, self).get_service_arguments()
args['factory'] = myapp.MyRootFactory
return args
@resource.register(viewset=MyViewSet())
class Resource(resource.UserResource):
mapping = 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.security import IAuthorizationPolicy
class MyRootFactory(object):
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(object):
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.