diff --git a/NEWS b/NEWS index 5c1ec1a..8a044da 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ -xxx-xx-2019: version 1.6.1 +xxx-xx-2019: version 1.7.0 + - Add schemas to Auth API (#134) - Using POST in copy to if the query in longer than 1024 bytes (#142) Jun-17-2019: version 1.6.0 diff --git a/carto/api_keys.py b/carto/api_keys.py index 53fa4c5..b79f1f1 100644 --- a/carto/api_keys.py +++ b/carto/api_keys.py @@ -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 @@ -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" @@ -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) @@ -146,15 +156,54 @@ 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. + For example if you grant `create` on the user `public` schema, they will be able to run `CREATE TABLE AS...` SQL queries + 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) + _expand = False 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', @@ -162,7 +211,8 @@ def get_id(self): }, { 'type': 'database', - 'tables': tables + 'tables': tables, + 'schemas': schemas }, { 'type': 'dataservices', diff --git a/carto/fields.py b/carto/fields.py index fe21252..ea1ed8a 100644 --- a/carto/fields.py +++ b/carto/fields.py @@ -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` @@ -76,6 +83,7 @@ class GrantsField(ResourceField): type_field = { 'apis': 'apis', 'tables': 'database', + 'schemas': 'database', 'services': 'dataservices' } @@ -87,7 +95,7 @@ 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]) + setattr(resource, field, grant_type.get(field, [])) instance.__dict__[self.name] = resource diff --git a/doc/source/auth_api.rst b/doc/source/auth_api.rst index 2ca6e76..1b9fd21 100644 --- a/doc/source/auth_api.rst +++ b/doc/source/auth_api.rst @@ -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. diff --git a/requirements.txt b/requirements.txt index 40d0c4e..9f1a419 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ requests>=2.7.0 -pyrestcli==0.6.11 +pyrestcli==0.6.12