示例#1
0
    def get_permission(cls, permission_name):
        """
        Get a permission for current model.

        Return a String.

        """
        return PERMISSION.get(cls, permission_name)
示例#2
0
    def get_default_permission_list(cls):
        """
        Get a list with default permissions for this model.

        Default permissions are 'create', 'read', 'update', 'delete'
        and 'action' (a.k.a. CRUDA).

        Return a List of strings.

        """
        return PERMISSION.cruda(cls)
示例#3
0
    def get_permission(self, model_name, permission_name):
        """
        Get a permission for a model.

        AttributeError is raised when PermissionData does not have
        the permission defined as attribute.

        Returns a permission or None.

        """
        perm = PERMISSION.get(model_name, permission_name)
        return self.instances.get(perm)
示例#4
0
    def get_permission_list(self, model_name, flags):
        """
        Get a list of permissions for a model.

        Returns a List of permissions.

        """
        permissions = []
        for perm in PERMISSION.cruda(model_name, flags=flags):
            permission = self.instances.get(perm)
            if permission and (permission not in permissions):
                permissions.append(permission)

        return permissions
示例#5
0
class ProjectSchema(BaseModelSchema):
    """
    Schema definition for Project model.

    """
    name = SchemaNode(String(), validator=Length(min=3))
    parent_id = SchemaNode(Integer(), missing=drop)
    client_id = SchemaNode(Integer(), missing=drop)
    user_id = SchemaNode(Integer())
    is_public = SchemaNode(Boolean(),
                           validator=RequirePermissions(
                               PERMISSION.get('project',
                                              'set_is_public',
                                              context=__name__)),
                           missing=drop)
示例#6
0
    def __acl__(cls):
        """
        ACL (Access Control List) with permission rules for current model.

        Rules apply only to Authenticated users.

        ACL follows CRUD for each rule (Create, Read, Update and Delete).

        Returns an ACL.

        """
        acl = []
        for permission in PERMISSION.cruda(cls, flags='crud'):
            rule = (Allow, Authenticated, permission)
            acl.append(rule)

        # Append default ACL rules
        acl.extend(DEFAULT_ACL)

        return acl
示例#7
0
def add_resource_describe(config, version, resource):
    """
    Add support to describe API resources.

    Describe can be called as `/@describe` in the API root URL.

    """
    route_name = 'api.{}.describe'.format(version)
    config.add_route(
        route_name,
        '/@describe',
        request_method='GET',
        factory=resource,
    )
    config.add_view(
        resource,
        route_name=route_name,
        renderer='json',
        permission=PERMISSION.get('api', 'describe', context=__name__),
    )
示例#8
0
 def get_extra_permissions(self):
     return (PERMISSION.get('api', 'describe'), )
示例#9
0
class BaseResource(object):
    """
    Base class for Sandglass time API resources.

    """
    interface.implements(IDescribable)

    # Name used as prefix for this resource URLs
    # NOTE: For REST APIs it is recommended to be in plural form
    name = None

    # Class used for describing a API resources
    describer_cls = ResourceDescriber

    @classmethod
    def get_route_prefix(cls):
        """
        Get prefix to be used for current resource URL path.

        Returns a String.

        """
        if not cls.name:
            raise Exception("Resource name can't be empty")

        return cls.name.lower()

    @classmethod
    def get_collection_path(cls):
        """
        Get collection URL path.

        Return a String.

        """
        route_info = REST_ROUTE_INFO['collection']
        route_name = route_info['route_name']
        member = cls.get_route_prefix()
        return route_path(route_name, member=member)

    @classmethod
    def get_member_path(cls, pk):
        """
        Get member URL path.

        Argument `pk` is the primary key value for the member object.

        Return a String.

        """
        route_info = REST_ROUTE_INFO['member']
        route_name = route_info['route_name']
        member = cls.get_route_prefix()
        return route_path(route_name, member=member, pk=pk)

    @classmethod
    def get_related_path(cls, pk, related_name):
        """
        Get member URL path.

        Argument `pk` is the primary key value for the member object,
        and `related_name` is the name of the related object(s).

        Return a String.

        """
        route_info = REST_ROUTE_INFO['related']
        route_name = route_info['route_name']
        member = cls.get_route_prefix()
        return route_path(
            route_name,
            member=member,
            pk=pk,
            related_name=related_name,
        )

    @classmethod
    def get_actions_by_type(cls, action_type):
        """
        Get the list of action information for current class.

        Action type can be member, collection or * for any type.

        Return a List of dictionaries.

        """
        action_info_list = []
        member_list = inspect.getmembers(cls, predicate=inspect.ismethod)
        for member in member_list:
            # Get action info from the method definition
            action_info = getattr(member[1], '__action__', None)
            if not action_info:
                continue

            # Check if current action info match action type
            if action_type != '*':
                if action_info.get('type') != action_type:
                    # Skip current action info when type is not right
                    continue

            action_info_list.append(action_info)

        return action_info_list

    def __init__(self, request):
        self.request = request

    def _get_pk_value(self):
        value = self.request.matchdict.get('pk')
        try:
            pk_value = int(value)
        except (ValueError, TypeError):
            pk_value = None

        return pk_value

    def _get_related_name(self):
        return self.request.matchdict.get('related_name')

    @property
    def is_member_request(self):
        """
        Check if current request is a member request.

        Method checks if pk_value is not None. When no pk value is
        available it means the current is a collection request.

        Return a Boolean.

        """
        return self.pk_value is not None

    @property
    def is_collection_request(self):
        """
        Check if current request is a collection request.

        Method checks if pk_value is None. When no pk value is
        available it means the current is a collection request.

        Return a Boolean.

        """
        return not self.is_member_request

    @property
    def is_related_request(self):
        """
        Check if current request is a related request.

        Method checks if pk_value is not None. When no pk value is
        available it means the current is a collection request.
        It also checks that related_name is available.

        Return a Boolean.

        """
        return self.is_member_request and self.related_name

    @reify
    def request_data(self):
        """
        Get JSON data from current request body.

        Returns a python representation of the JSON.

        """
        # When request body is empty skip JSON decoding
        if not self.request.body:
            return

        try:
            return self.request.json_body
        except ValueError:
            # Exception is also raised when "Content Type"
            # is not "application/json".
            LOG.exception('Invalid JSON in request body')
            raise APIRequestDataError()

    @reify
    def pk_value(self):
        """
        Get primary key value for current request.

        Return an Integer or None.

        """
        return self._get_pk_value()

    @reify
    def related_name(self):
        """
        Get related name when it is available in the URL.

        When no related name is given or the name is not a model
        relationship `NotFound` is raised.

        Return a String.

        """
        return self._get_related_name()

    def get_filter_from_to(self):
        """
        Get from/to request arguments as python dates.

        ValueError is raised when date format is invalid for one
        or both dates.

        Return a Tuple.

        """
        from_date = self.request.params.get('from')
        if from_date:
            # Convert string to date
            from_date = datetime.strptime(from_date, ISO_DATE_FORMAT)

        to_date = self.request.params.get('to')
        if to_date:
            # Convert string to date
            to_date = datetime.strptime(to_date, ISO_DATE_FORMAT)

        return (from_date, to_date)

    @collection_action(
        methods='GET',
        permission=PERMISSION.get('api', 'describe'),
    )
    def describe(self):
        """
        Get an API resource description.

        """
        describer = self.describer_cls(self)
        return describer