Esempio n. 1
0
 def get_permissions(self, user, loaded):
     hoisted_read = Permissions.get_permissions(
         user, loaded[-1]) & Permissions.READ
     if self.resource:
         if super().get_permissions(user, loaded) != Permissions.ADMIN:
             loaded = loaded[:-1]
     return super().get_permissions(user, loaded) | hoisted_read
Esempio n. 2
0
 def _validate_patch(self, patch):
     for blob in patch:
         if not blob['path'].split('/')[1] in Namespace.WHITELIST:
             raise exceptions.InvalidField(blob['path'])
         if blob.get('value') and blob['path'].startswith('/permissions'):
             blob['value'] = Permissions.from_string(blob['value'])
     return patch
Esempio n. 3
0
 def _validate_patch(self, patch):
     for blob in patch:
         if not blob['path'].split('/')[1] in Namespace.WHITELIST:
             raise exceptions.InvalidField(blob['path'])
         if blob.get('value') and blob['path'].startswith('/permissions'):
             blob['value'] = Permissions.from_string(blob['value'])
     return patch
Esempio n. 4
0
    def create_collection(self, name, user, logger=None, storage=None, state=None, permissions=None, schema=None, plugins=None, **kwargs):
        if kwargs:
            raise exceptions.InvalidFields(kwargs.keys())

        uid = str(uuid.uuid4()).replace('-', '')
        state = state or settings.COLLECTION_BACKENDS['state']
        logger = logger or settings.COLLECTION_BACKENDS['logger']
        storage = storage or settings.COLLECTION_BACKENDS['storage']

        if isinstance(permissions or {}, dict):
            try:
                permissions = {
                    key: Permissions.from_string(value)
                    for key, value in (permissions or {}).items()
                }
                permissions[user] = Permissions.ADMIN
            except KeyError as e:
                raise exceptions.InvalidPermission(e.args[0])
            except AttributeError:
                pass  # Schema validation will catch issues

        collection_dict = {
            'uuid': uid,
            'plugins': plugins or {},
            'schema': schema,
            'permissions': permissions,
            'logger': {
                'backend': logger,
                'settings': get_backend(logger).settings_for(self.uuid, uid, 'logger')
            },
            'state': {
                'backend': state,
                'settings': get_backend(state).settings_for(self.uuid, uid, 'state')
            },
            'storage': {
                'backend': storage,
                'settings': get_backend(storage).settings_for(self.uuid, uid, 'storage')
            }
        }

        # Validate that our inputs can actually be deserialized to a collection
        Collection(Document(
            ref=name,
            log_ref=None,
            data_ref=None,
            created_on=0,
            created_by='',
            modified_on=0,
            modified_by='',
            data=collection_dict,
        ))

        try:
            return Collection(self.create(name, collection_dict, user))
        except exceptions.KeyExists:
            raise exceptions.KeyExists(
                code='C409',
                title='Collection already exists',
                detail='Collection "{}" already exists in namespace "{}"'.format(name, self.ref)
            )
Esempio n. 5
0
    def prepare(self):
        super().prepare()
        if self.request.method == 'OPTIONS':
            return  # Dont do anything for OPTIONS requests

        resources = []
        resource = self.resource

        while resource:
            resources = [resource] + resources
            resource = resource.parent

        loaded = [
            r.load(self.path_kwargs[r.name + '_id'], self.request)
            for r in resources
            if self.path_kwargs.get(r.name + '_id')
        ]

        self.permissions = Permissions.get_permissions(self.current_user, *loaded)

        # TODO this is kinda hacky
        self.current_user.permissions = self.permissions

        # TODO 404s get raised before permissions are checked
        required_permissions = self.resource.get_permissions(self.request)
        if (required_permissions & self.permissions) != required_permissions:
            if self.current_user.uid is None:
                raise exceptions.Unauthorized()
            raise exceptions.Forbidden(required_permissions)
Esempio n. 6
0
 def attributes(self):
     return {
         'name': self._instance.ref,
         'permissions': {
             sel: Permissions(perm).name
             for sel, perm in self._instance.data['permissions'].items()
         }
     }
Esempio n. 7
0
 def attributes(cls, inst):
     return {
         'name': inst.ref,
         'permissions': {
             sel: Permissions(perm).name
             for sel, perm in inst.data['permissions'].items()
         }
     }
Esempio n. 8
0
 def attributes(cls, inst):
     return {
         'name': inst.ref,
         'schema': inst.data.get('schema'),
         # 'documentCreatorPermissions': Permissions(inst.data['documentCreatorPermissions']).name,
         'permissions': {
             sel: Permissions(perm).name
             for sel, perm in inst.data['permissions'].items()
         }
     }
Esempio n. 9
0
    def create_namespace(self, name, user, permissions=None, **kwargs):
        if kwargs:
            raise exceptions.InvalidFields(kwargs.keys())

        uid = str(uuid.uuid4()).replace('-', '')

        if isinstance(permissions or {}, dict):
            try:
                permissions = {
                    key: Permissions.from_string(value)
                    for key, value in (permissions or {}).items()
                }
                permissions[user] = Permissions.ADMIN
            except KeyError as e:
                raise exceptions.InvalidPermission(e.args[0])
            except AttributeError:
                pass  # Schema validation will catch issues

        try:
            self.create(
                name, {
                    'uuid': uid,
                    'permissions': {
                        **(permissions or {}), user: Permissions.ADMIN
                    },
                    'logger': {
                        'backend':
                        settings.NAMESPACE_BACKENDS['logger'],
                        'settings':
                        get_backend(settings.NAMESPACE_BACKENDS['logger']).
                        settings_for(self.uuid, uid, 'logger')
                    },
                    'state': {
                        'backend':
                        settings.NAMESPACE_BACKENDS['state'],
                        'settings':
                        get_backend(
                            settings.NAMESPACE_BACKENDS['state']).settings_for(
                                self.uuid, uid, 'state')
                    },
                    'storage': {
                        'backend':
                        settings.NAMESPACE_BACKENDS['storage'],
                        'settings':
                        get_backend(settings.NAMESPACE_BACKENDS['storage']).
                        settings_for(self.uuid, uid, 'storage')
                    }
                }, user)
        except exceptions.KeyExists:
            raise exceptions.KeyExists(
                code='N409',
                title='Namespace already exists',
                detail='Namespace "{}" already exists'.format(name))

        return self.get_namespace(name)
Esempio n. 10
0
    def attributes(self):
        full = self._permission == Permissions.ADMIN or (
            (self._permission ^ Permissions.get_permissions(
                self._user, Collection(self._instance)))
            & Permissions.READ) == Permissions.READ

        return {
            'name':
            self._instance.ref,
            'schema':
            self._instance.data.get('schema'),
            **({} if not full else {
                   'plugins': self._instance.data.get('plugins', {}),
                   'permissions': {
                       sel: Permissions(perm).name
                       for sel, perm in self._instance.data['permissions'].items(
                       )
                   }
               })
        }
Esempio n. 11
0
    async def patch(self, handler):
        if not handler.payload:
            raise exceptions.BadRequest()

        if not handler.payload.attributes.get('id'):
            raise exceptions.BadRequest(detail='Id must be provided')

        error = None
        id = handler.payload.attributes.pop('id')

        try:
            doc = self.collection.read(id)
        except exceptions.NotFound as e:
            error = e

        permissions = handler.current_user.permissions | Permissions.get_permissions(
            handler.current_user, doc)
        if error or not (permissions & Permissions.UPDATE):
            return handler.write(
                {'data': {
                    'id': id,
                    'type': 'suppressions',
                    'attributes': {}
                }})

        email = self.extract_email(doc=doc)
        headers = {'Authorization': 'Bearer {}'.format(self.sendgrid_key)}

        for group, subscribe in list(handler.payload.attributes.items()):
            if subscribe:
                async with aiohttp.post(
                        'https://api.sendgrid.com/v3/asm/groups/{}/suppressions'
                        .format(group),
                        headers=headers,
                        data=json.dumps({'recipient_emails':
                                         [email]})) as response:
                    assert response.status == 201  # TODO Handle errors
            else:
                async with aiohttp.delete(
                        'https://api.sendgrid.com/v3/asm/groups/{}/suppressions/{}'
                        .format(group, quote(email)),
                        headers=headers) as response:
                    assert response.status == 204  # TODO Handle errors
            handler.payload.attributes[group] = bool(subscribe)

        return handler.write({
            'data': {
                'id': id,
                'type': 'supressions',
                'attributes': handler.payload.attributes
            }
        })
Esempio n. 12
0
    def attributes(self):
        full = self._permission == Permissions.ADMIN or ((self._permission ^ Permissions.get_permissions(self._user, Collection(self._instance))) & Permissions.READ) == Permissions.READ

        return {
            'name': self._instance.ref,
            'schema': self._instance.data.get('schema'),
            **({} if not full else {
                'plugins': self._instance.data.get('plugins', {}),
                'permissions': {
                    sel: Permissions(perm).name
                    for sel, perm in self._instance.data['permissions'].items()
                }
            })
        }
Esempio n. 13
0
 def meta(self):
     return {
         'permissions':
         Permissions(self._permission).name,
         'created-by':
         self._instance.created_by,
         'modified-by':
         self._instance.modified_by,
         'created-on':
         datetime.datetime.fromtimestamp(
             self._instance.created_on).isoformat(),
         'modified-on':
         datetime.datetime.fromtimestamp(
             self._instance.modified_on).isoformat()
     }
Esempio n. 14
0
    def prepare(self):
        super().prepare()
        if self.request.method == 'OPTIONS':
            return  # Dont do anything for OPTIONS requests

        loaded = []
        try:
            for view in self._view_class.lineage():
                key = view.name + '_id'
                if self.path_kwargs[key] is None:
                    break
                loaded.append(view.load(self.path_kwargs[key], *loaded))
        except exceptions.NotFound as e:
            err = e
            # Load as many resources as are available to do a permissions check
            # A 404 will be thrown if the user has the required permissions
            self._view = view(*loaded)
        else:
            err = None
            self._view = self._view_class(*loaded)

            # If this is a relationship swap out the current view with the relation
            if 'relationship' in self.path_kwargs:
                relationship = self._serializer.relations[self.path_kwargs['relationship']]
                self._view = relationship.view(*loaded)
                self._serializer = relationship.serializer()

        permissions = Permissions.get_permissions(self.current_user, *loaded)
        required_permissions = self._view.get_permissions(self.request)

        # For use later on
        self.current_user.permissions = permissions

        # Check permissions
        if (required_permissions & permissions) != required_permissions:
            if self.current_user.uid is None:
                raise exceptions.Unauthorized()
            raise exceptions.Forbidden(required_permissions)

        # Not found is always raised AFTER permissions checks
        if err:
            raise err

        if self.request.method in ('GET', 'DELETE'):
            return  # GET and DELETE bodies are ignored

        self.payload  # Force payload to load and validate
Esempio n. 15
0
    async def patch(self, handler):
        if not handler.payload:
            raise exceptions.BadRequest()

        if not handler.payload.attributes.get('id'):
            raise exceptions.BadRequest(detail='Id must be provided')

        error = None
        id = handler.payload.attributes.pop('id')

        try:
            doc = self.collection.read(id)
        except exceptions.NotFound as e:
            error = e

        permissions = handler.current_user.permissions | Permissions.get_permissions(handler.current_user, doc)
        if error or not (permissions & Permissions.UPDATE):
            return handler.write({
                'data': {
                    'id': id,
                    'type': 'suppressions',
                    'attributes': {}
                }
            })

        email = self.extract_email(doc=doc)
        headers = {'Authorization': 'Bearer {}'.format(self.sendgrid_key)}

        for group, subscribe in list(handler.payload.attributes.items()):
            if subscribe:
                async with aiohttp.post('https://api.sendgrid.com/v3/asm/groups/{}/suppressions'.format(group), headers=headers, data=json.dumps({'recipient_emails': [email]})) as response:
                    assert response.status == 201  # TODO Handle errors
            else:
                async with aiohttp.delete('https://api.sendgrid.com/v3/asm/groups/{}/suppressions/{}'.format(group, quote(email)), headers=headers) as response:
                    assert response.status == 204  # TODO Handle errors
            handler.payload.attributes[group] = bool(subscribe)

        return handler.write({
            'data': {
                'id': id,
                'type': 'supressions',
                'attributes': handler.payload.attributes
            }
        })
Esempio n. 16
0
    async def get(self, handler):
        error = None
        id = handler.get_query_argument('id')

        try:
            doc = self.collection.read(id)
        except exceptions.NotFound as e:
            error = e

        permissions = handler.current_user.permissions | Permissions.get_permissions(
            handler.current_user, doc)

        if error or not (permissions & Permissions.READ):
            return handler.write(
                {'data': {
                    'id': id,
                    'type': 'suppressions',
                    'attributes': {}
                }})

        attrs = {}
        email = self.extract_email(doc=doc)
        groups = handler.get_query_arguments('group[]')

        for group in groups:
            async with aiohttp.get(
                    'https://api.sendgrid.com/v3/asm/groups/{}/suppressions'.
                    format(group),
                    headers={
                        'Authorization': 'Bearer {}'.format(self.sendgrid_key)
                    }) as response:
                if response.status != 200 or not isinstance(
                        await response.json(), list):
                    attrs[group] = False
                else:
                    attrs[group] = email in await response.json()

        return handler.write(
            {'data': {
                'id': id,
                'type': 'supressions',
                'attributes': attrs
            }})
Esempio n. 17
0
    def create_namespace(self, name, user, permissions=None, **kwargs):
        if kwargs:
            raise exceptions.InvalidFields(kwargs.keys())

        uid = str(uuid.uuid4()).replace('-', '')

        if isinstance(permissions or {}, dict):
            try:
                permissions = {
                    key: Permissions.from_string(value)
                    for key, value in (permissions or {}).items()
                }
                permissions[user] = Permissions.ADMIN
            except KeyError as e:
                raise exceptions.InvalidPermission(e.args[0])
            except AttributeError:
                pass  # Schema validation will catch issues

        try:
            self.create(name, {
                'uuid': uid,
                'permissions': {**(permissions or {}), user: Permissions.ADMIN},
                'logger': {
                    'backend': settings.NAMESPACE_BACKENDS['logger'],
                    'settings': get_backend(settings.NAMESPACE_BACKENDS['logger']).settings_for(self.uuid, uid, 'logger')
                },
                'state': {
                    'backend': settings.NAMESPACE_BACKENDS['state'],
                    'settings': get_backend(settings.NAMESPACE_BACKENDS['state']).settings_for(self.uuid, uid, 'state')
                },
                'storage': {
                    'backend': settings.NAMESPACE_BACKENDS['storage'],
                    'settings': get_backend(settings.NAMESPACE_BACKENDS['storage']).settings_for(self.uuid, uid, 'storage')
                }
            }, user)
        except exceptions.KeyExists:
            raise exceptions.KeyExists(
                code='N409',
                title='Namespace already exists',
                detail='Namespace "{}" already exists'.format(name)
            )

        return self.get_namespace(name)
Esempio n. 18
0
    def update(self, key, patch, user):
        if isinstance(patch, dict):
            keys = set(patch.keys())
            if not keys.issubset(Collection.WHITELIST):
                raise exceptions.InvalidFields(keys - Collection.WHITELIST)

            previous = self._state.get(key)
            patch = jsonpatch.JsonPatch.from_diff(previous.data, {**previous.data, **patch})
            patch = list(filter(lambda p: p['path'].split('/')[1] in Namespace.WHITELIST, patch))

        for blob in patch:
            if not blob['path'].split('/')[1] in Collection.WHITELIST:
                raise exceptions.InvalidField(blob['path'])
            if blob.get('value') and not isinstance(blob.get('value'), Permissions) and blob['path'].startswith('/permissions'):
                try:
                    blob['value'] = Permissions(reduce(operator.or_, [Permissions[p.strip()] for p in blob['value'].split(',')], Permissions.NONE))
                except (AttributeError, KeyError):
                    raise exceptions.InvalidPermission(blob['value'])

        return super().update(key, patch, user)
Esempio n. 19
0
    async def get(self, handler):
        error = None
        id = handler.get_query_argument('id')

        try:
            doc = self.collection.read(id)
        except exceptions.NotFound as e:
            error = e

        permissions = handler.current_user.permissions | Permissions.get_permissions(handler.current_user, doc)

        if error or not (permissions & Permissions.READ):
            return handler.write({
                'data': {
                    'id': id,
                    'type': 'suppressions',
                    'attributes': {}
                }
            })

        attrs = {}
        email = self.extract_email(doc=doc)
        groups = handler.get_query_arguments('group[]')

        for group in groups:
            async with aiohttp.get('https://api.sendgrid.com/v3/asm/groups/{}/suppressions'.format(group), headers={'Authorization': 'Bearer {}'.format(self.sendgrid_key)}) as response:
                if response.status != 200 or not isinstance(await response.json(), list):
                    attrs[group] = False
                else:
                    attrs[group] = email in await response.json()

        return handler.write({
            'data': {
                'id': id,
                'type': 'supressions',
                'attributes': attrs
            }
        })
Esempio n. 20
0
 def get_permissions(self, request):
     return Permissions.from_method(request.method)
Esempio n. 21
0
 def __init__(self, request, user, inst, *parents):
     super().__init__(request, user, inst, *parents)
     self._permission |= Permissions.get_permissions(user, Namespace(inst))
Esempio n. 22
0
 def collection_permissions(self):
     return Permissions.from_string(self._raw.get('collection', ''))
Esempio n. 23
0
 def document_permissions(self):
     return Permissions.from_string(self._raw.get('document', ''))
Esempio n. 24
0
 def get_permissions(self, user, loaded):
     hoisted_read = Permissions.get_permissions(user, loaded[-1]) & Permissions.READ
     if self.resource:
         if super().get_permissions(user, loaded) != Permissions.ADMIN:
             loaded = loaded[:-1]
     return super().get_permissions(user, loaded) | hoisted_read
Esempio n. 25
0
 def __init__(self, request, user, inst, *parents):
     super().__init__(request, user, inst, *parents)
     self._permission |= Permissions.get_permissions(user, Namespace(inst))
Esempio n. 26
0
 def document_permissions(self):
     return Permissions.from_string(self._raw.get('document', ''))
Esempio n. 27
0
 def get_permissions(self, request):
     return Permissions.from_method(request.method)
Esempio n. 28
0
 def get_permissions(self, user, loaded):
     return Permissions.get_permissions(user, *loaded)
Esempio n. 29
0
 def collection_permissions(self):
     return Permissions.from_string(self._raw.get('collection', ''))
Esempio n. 30
0
    def create_collection(self,
                          name,
                          user,
                          logger=None,
                          storage=None,
                          state=None,
                          permissions=None,
                          schema=None,
                          plugins=None,
                          **kwargs):
        if kwargs:
            raise exceptions.InvalidFields(kwargs.keys())

        uid = str(uuid.uuid4()).replace('-', '')
        state = state or settings.COLLECTION_BACKENDS['state']
        logger = logger or settings.COLLECTION_BACKENDS['logger']
        storage = storage or settings.COLLECTION_BACKENDS['storage']

        if isinstance(permissions or {}, dict):
            try:
                permissions = {
                    key: Permissions.from_string(value)
                    for key, value in (permissions or {}).items()
                }
                permissions[user] = Permissions.ADMIN
            except KeyError as e:
                raise exceptions.InvalidPermission(e.args[0])
            except AttributeError:
                pass  # Schema validation will catch issues

        collection_dict = {
            'uuid': uid,
            'plugins': plugins or {},
            'schema': schema,
            'permissions': permissions,
            'logger': {
                'backend':
                logger,
                'settings':
                get_backend(logger).settings_for(self.uuid, uid, 'logger')
            },
            'state': {
                'backend':
                state,
                'settings':
                get_backend(state).settings_for(self.uuid, uid, 'state')
            },
            'storage': {
                'backend':
                storage,
                'settings':
                get_backend(storage).settings_for(self.uuid, uid, 'storage')
            }
        }

        # Validate that our inputs can actually be deserialized to a collection
        Collection(
            Document(
                ref=name,
                log_ref=None,
                data_ref=None,
                created_on=0,
                created_by='',
                modified_on=0,
                modified_by='',
                data=collection_dict,
            ))

        try:
            return Collection(self.create(name, collection_dict, user))
        except exceptions.KeyExists:
            raise exceptions.KeyExists(
                code='C409',
                title='Collection already exists',
                detail='Collection "{}" already exists in namespace "{}"'.
                format(name, self.ref))