Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support schemas grants in Auth API #134

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
xxx-xx-2019: version 1.7.0
- Add schemas to Auth API (#134)

Jun-17-2019: version 1.6.0
- Auth API (#94)
- Kuviz API (#121 #124)
Expand Down
59 changes: 54 additions & 5 deletions carto/api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from pyrestcli.fields import CharField, DateTimeField

from .fields import TableGrantField, GrantsField
from .fields import TableGrantField, GrantsField, SchemaGrantField
from .resources import Resource, Manager
from .exceptions import CartoException
from .paginators import CartoPaginator
Expand All @@ -29,6 +29,7 @@
PERMISSION_SELECT = "select"
PERMISSION_UPDATE = "update"
PERMISSION_DELETE = "delete"
PERMISSION_DELETE = "create"
SERVICE_GEOCODING = "geocoding"
SERVICE_ROUTING = "routing"
SERVICE_ISOLINES = "isolines"
Expand Down Expand Up @@ -79,32 +80,41 @@ class APIKeyManager(Manager):
json_collection_attribute = "result"
paginator_class = CartoPaginator

def create(self, name, apis=['sql', 'maps'], tables=None, services=None):
def create(self, name, apis=['sql', 'maps'], tables=None, schemas=None, services=None):
"""
Creates a regular APIKey.

:param name: The API key name
:param apis: Describes which APIs does this API Key provide access to
:param tables: Describes to which tables and which privleges on each table this API Key grants access to
:param schemas: Describes to which schemas and which privleges on each schema this API Key grants access to
:param services: Describes to which data services this API Key grants access to
:type name: str
:type apis: list
:type tables: TableGrant or dict
:type schemas: SchemaGrant or dict
:type services: list

:return: An APIKey instance with a token
"""
grants = []
database_grant = {'type': 'database'}
if not apis:
raise CartoException("'apis' cannot be empty. Please specify which CARTO APIs you want to grant. Example: ['sql', 'maps']")
grants.append({'type': 'apis', 'apis': apis})
if tables and (len(tables) > 0):
if isinstance(tables[0], dict):
grants.append({'type': 'database', 'tables': tables})
database_grant['tables'] = tables
elif isinstance(tables[0], TableGrant):
grants.append({'type': 'database', 'tables': [x.to_json for x in tables]})
database_grant['tables'] = [x.to_json() for x in tables]
if schemas and (len(schemas) > 0):
if isinstance(schemas[0], dict):
database_grant['schemas'] = schemas
elif isinstance(schemas[0], SchemaGrant):
database_grant['schemas'] = [x.to_json() for x in schemas]
if services:
grants.append({'type': 'dataservices', 'services': services})
grants.append(database_grant)
return super(APIKeyManager, self).create(name=name, grants=grants)


Expand Down Expand Up @@ -146,23 +156,62 @@ def to_json(self):
}


class SchemaGrant(Resource):
"""
Describes to which schemas and which privleges on each schema this API Key grants access to trough schemas attribute.
alrocar marked this conversation as resolved.
Show resolved Hide resolved
For example if you grant `create` on the user `public` schema, they will be able to run `CREATE TABLE AS...` SQL queries
alrocar marked this conversation as resolved.
Show resolved Hide resolved
This is an internal data type, with no specific API endpoints

See https://carto.com/developers/auth-api/reference/#section/API-Key-format

Example:

.. code::

{
"type": "database",
"schemas": [
{
"name": "public",
"permissions": [
"create"
]
}
]
}
"""
name = CharField()
permissions = CharField(many=True)

def to_json(self):
return {
'name': self.name,
'permissions': self.permissions
}


class Grants(Resource):
apis = CharField(many=True)
tables = TableGrantField(many=True)
services = CharField(many=True)
schemas = SchemaGrantField(many=True)

def get_id(self):
tables = []
schemas = []
if self.tables:
tables = [x.to_json() for x in self.tables]
if self.schemas:
schemas = [x.to_json() for x in self.schemas]
return [
{
'type': 'apis',
'apis': self.apis or []
},
{
'type': 'database',
'tables': tables
'tables': tables,
'schemas': schemas
},
{
'type': 'dataservices',
Expand Down
13 changes: 12 additions & 1 deletion carto/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ class TableGrantField(ResourceField):
value_class = "carto.api_keys.TableGrant"


class SchemaGrantField(ResourceField):
"""
:class:`carto.api_keys.SchemaGrant`
"""
value_class = "carto.api_keys.SchemaGrant"


class GrantsField(ResourceField):
"""
:class:`carto.api_keys.Grants`
Expand All @@ -76,6 +83,7 @@ class GrantsField(ResourceField):
type_field = {
'apis': 'apis',
'tables': 'database',
'schemas': 'database',
'services': 'dataservices'
}

Expand All @@ -87,7 +95,10 @@ def __set__(self, instance, value):
for field in resource.fields:
for grant_type in value:
if grant_type['type'] == self.type_field[field]:
setattr(resource, field, grant_type[field])
try:
setattr(resource, field, grant_type[field])
except KeyError:
pass
simon-contreras-deel marked this conversation as resolved.
Show resolved Hide resolved

instance.__dict__[self.name] = resource

Expand Down
2 changes: 1 addition & 1 deletion doc/source/auth_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Every API key consists on four main parts:
- grants: Describes which APIs this API key provides access to and to which tables. It consists on an array of two JSON objects. This object's `type` attribute can be `apis`, `database` or `dataservices`:

- `apis`: Describes which APIs does this API key provide access to through apis attribute
- `database`: Describes to which tables and which privleges on each table this API key grants access to though tables attribute
- `database`: Describes to which tables and schemas and which privleges on them this API Key grants access to through `tables` and `schemas` attributes. You can grant read (`select`) or write (`insert`, `update`, `delete`) permissions on tables. For the case of `schemas` once granted the `create` permission on a schema you'll be able to run SQL queries such as `CREATE TABLE AS...`, `CREATE VIEW AS...` etc. to create entities on it. As the owner of those tables, the API key will be able to `DROP` or `ALTER` them as well.
- `dataservices`: Describes to which data services this API key grants access to though services attribute:

See the `full API key format reference`_ in the CARTO help center for more info about allowed table permissions, `dataservices`, etc.
Expand Down