def get_permission(cls, permission_name): """ Get a permission for current model. Return a String. """ return PERMISSION.get(cls, permission_name)
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)
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)
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
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)
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
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__), )
def get_extra_permissions(self): return (PERMISSION.get('api', 'describe'), )
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