コード例 #1
0
def create_sample_params(
        params: Dict[str,
                     Any]) -> Tuple[Sample, Optional[UUID], Optional[int]]:
    '''
    Process the input from the create_sample API call and translate it into standard types.

    :param params: The unmarshalled JSON recieved from the API as part of the create_sample
        call.
    :returns: A tuple of the sample to save, the UUID of the sample for which a new version should
        be created or None if an entirely new sample should be created, and the previous version
        of the sample expected when saving a new version.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    _check_params(params)
    if type(params.get('sample')) != dict:
        raise _IllegalParameterError(
            'params must contain sample key that maps to a structure')
    s = params['sample']
    if type(s.get('node_tree')) != list:
        raise _IllegalParameterError(
            'sample node tree must be present and a list')
    if s.get('name') is not None and type(s.get('name')) != str:
        raise _IllegalParameterError('sample name must be omitted or a string')
    nodes = _check_nodes(s)
    id_ = get_id_from_object(s, ID, name='sample.id')

    pv = params.get('prior_version')
    if pv is not None and type(pv) != int:
        raise _IllegalParameterError(
            'prior_version must be an integer if supplied')
    s = Sample(nodes, s.get('name'))
    return (s, id_, pv)
コード例 #2
0
def get_id_from_object(obj: Dict[str, Any], key, name=None, required=False) -> Optional[UUID]:
    '''
    Given a dict, get a sample ID from the dict if it exists.

    If None or an empty dict is passed to the method, None is returned.

    :param obj: the dict wherein the ID can be found.
    :param key: the key in the dict for the ID value.
    :param name: the name of the ID to use in an exception, defaulting to the key.
    :param required: if no ID is present, throw an exception.
    :returns: the ID, if it exists, or None.
    :raises MissingParameterError: if the ID is required but not present.
    :raises IllegalParameterError: if the ID is provided but is invalid.
    '''
    id_ = None
    _check_string(key, 'key')
    name = name if name else key
    if required and (not obj or not obj.get(key)):
        raise _MissingParameterError(name)
    if obj and obj.get(key):
        if type(obj[key]) != str:
            raise _IllegalParameterError(f'{name} {obj[key]} must be a UUID string')
        try:
            id_ = UUID(obj[key])
        except ValueError as _:  # noqa F841
            raise _IllegalParameterError(f'{name} {obj[key]} must be a UUID string')
    return id_
コード例 #3
0
def get_id_from_object(obj: Dict[str, Any], required=False) -> Optional[UUID]:
    '''
    Given a dict, get a sample ID from the dict if it exists, using the key 'id'.

    If None or an empty dict is passed to the method, None is returned.

    :param obj: The dict wherein the ID can be found.
    :param required: If no ID is present, throw an exception.
    :returns: The ID, if it exists, or None.
    :raises MissingParameterError: If the ID is required but not present.
    :raises IllegalParameterError: If the ID is provided but is invalid.
    '''
    id_ = None
    if required and (not obj or not obj.get(ID)):
        raise _MissingParameterError('Sample ID')
    if obj and obj.get(ID):
        if type(obj[ID]) != str:
            raise _IllegalParameterError(
                f'Sample ID {obj[ID]} must be a UUID string')
        try:
            id_ = UUID(obj[ID])
        except ValueError as _:  # noqa F841
            raise _IllegalParameterError(
                f'Sample ID {obj[ID]} must be a UUID string')
    return id_
コード例 #4
0
def validate_samples_params(params: Dict[str, Any]) -> List[Sample]:
    '''
    Process the input from the validate_samples API call and translate it into standard types.

    :param params: The unmarshalled JSON recieved from the API as part of the create_sample
        call.
    :returns: A tuple of the sample to save, the UUID of the sample for which a new version should
        be created or None if an entirely new sample should be created, and the previous version
        of the sample expected when saving a new version.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    _check_params(params)
    if type(params.get('samples')) != list or len(params.get('samples',
                                                             [])) == 0:
        raise _IllegalParameterError('params must contain list of `samples`')
    samples = []
    for s in params['samples']:
        if type(s.get('node_tree')) != list:
            raise _IllegalParameterError(
                'sample node tree must be present and a list')
        if type(s.get('name')) != str or len(s.get('name')) <= 0:
            raise _IllegalParameterError(
                'sample name must be included as non-empty string')
        nodes = _check_nodes(s)
        s = Sample(nodes, s.get('name'))
        samples.append(s)

    return samples
コード例 #5
0
def _check_meta(m, index,
                name) -> Optional[Dict[str, Dict[str, PrimitiveType]]]:
    if not m:
        return None
    if type(m) != dict:
        raise _IllegalParameterError(
            f"Node at index {index}'s {name} entry must be a mapping")
    for k1 in m:
        if type(k1) != str:
            raise _IllegalParameterError(
                f"Node at index {index}'s {name} entry contains a non-string key"
            )
        if type(m[k1]) != dict:
            raise _IllegalParameterError(
                f"Node at index {index}'s {name} entry does " +
                f"not have a dict as a value at key {k1}")
        for k2 in m[k1]:
            if type(k2) != str:
                raise _IllegalParameterError(
                    f"Node at index {index}'s {name} entry contains " +
                    f'a non-string key under key {k1}')
            v = m[k1][k2]
            if type(v) != str and type(v) != int and type(v) != float and type(
                    v) != bool:
                raise _IllegalParameterError(
                    f"Node at index {index}'s {name} entry does " +
                    f"not have a primitive type as the value at {k1}/{k2}")
    return m
コード例 #6
0
def _check_nodes(s):
    nodes = []
    for i, n in enumerate(s['node_tree']):
        if type(n) != dict:
            raise _IllegalParameterError(
                f'Node at index {i} is not a structure')
        if type(n.get('id')) != str:
            raise _IllegalParameterError(
                f'Node at index {i} must have an id key that maps to a string')
        try:
            type_ = _SubSampleType(n.get('type'))
        except ValueError:
            raise _IllegalParameterError(
                f'Node at index {i} has an invalid sample type: {n.get("type")}'
            )
        if n.get('parent') and type(n.get('parent')) != str:
            raise _IllegalParameterError(
                f'Node at index {i} has a parent entry that is not a string')
        mc = _check_meta(n.get('meta_controlled'), i, 'controlled metadata')
        mu = _check_meta(n.get('meta_user'), i, 'user metadata')
        sm = _check_source_meta(n.get('source_meta'), i)
        try:
            nodes.append(
                _SampleNode(n.get('id'), type_, n.get('parent'), mc, mu, sm))
            # already checked for the missing param error above, for id
        except _IllegalParameterError as e:
            raise _IllegalParameterError(f'Error for node at index {i}: ' +
                                         _cast(str, e.message)) from e
    return nodes
コード例 #7
0
def get_static_key_metadata_params(
        params: Dict[str, Any]) -> Tuple[List[str], Optional[bool]]:
    '''
    Given a dict, extract the parameters to interrogate metadata key static metadata.

    :param params: The parameters.
    :returns: A tuple consisting of, firstly, the list of keys to interrogate, and secondly,
        a trinary value where False indicates that standard keys should be interrogated, None
        indicates that prefix keys should be interrogated but only exact matches should be
        considered, and True indicates that prefix keys should be interrogated but prefix matches
        should be included in the results.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    _check_params(params)
    keys = params.get('keys')
    if type(keys) != list:
        raise _IllegalParameterError('keys must be a list')
    for i, k in enumerate(_cast(List[Any], keys)):
        if type(k) != str:
            raise _IllegalParameterError(f'index {i} of keys is not a string')
    prefix = params.get('prefix')
    pre: Optional[bool]
    if not prefix:
        pre = False
    elif prefix == 1:
        pre = None
    elif prefix == 2:
        pre = True
    else:
        raise _IllegalParameterError(f'Unexpected value for prefix: {prefix}')
    return _cast(List[str], keys), pre
コード例 #8
0
def _check_acl_duplicates(admin, write, read):
    for u in admin:
        if u in write or u in read:
            raise _IllegalParameterError(f'User {u} appears in two ACLs')
    for u in write:
        if u in read:
            raise _IllegalParameterError(f'User {u} appears in two ACLs')
コード例 #9
0
ファイル: workspace.py プロジェクト: jsfillman/sample_service
 def _get_ws_num(self, int_: str, upa):
     try:
         i = int(int_)
         if i < 1:
             raise _IllegalParameterError(f'{upa} is not a valid UPA')
         return i
     except ValueError:
         raise _IllegalParameterError(f'{upa} is not a valid UPA')
コード例 #10
0
def _get_acl(acls, type_):
    ret = []
    if acls.get(type_) is not None:
        acl = acls[type_]
        if type(acl) != list:
            raise _IllegalParameterError(f'{type_} ACL must be a list')
        for i, item, in enumerate(acl):
            if type(item) != str:
                raise _IllegalParameterError(f'Index {i} of {type_} ACL does not contain a string')
            ret.append(UserID(item))
    return ret
コード例 #11
0
def create_sample_params(
        params: Dict[str,
                     Any]) -> Tuple[Sample, Optional[UUID], Optional[int]]:
    '''
    Process the input from the create_sample API call and translate it into standard types.

    :param params: The unmarshalled JSON recieved from the API as part of the create_sample
        call.
    :returns: A tuple of the sample to save, the UUID of the sample for which a new version should
        be created or None if an entirely new sample should be created, and the previous version
        of the sample expected when saving a new version.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    _check_params(params)
    if type(params.get('sample')) != dict:
        raise _IllegalParameterError(
            'params must contain sample key that maps to a structure')
    s = params['sample']
    if type(s.get('node_tree')) != list:
        raise _IllegalParameterError(
            'sample node tree must be present and a list')
    if s.get('name') is not None and type(s.get('name')) != str:
        raise _IllegalParameterError('sample name must be omitted or a string')
    nodes = []
    for i, n in enumerate(s['node_tree']):
        if type(n) != dict:
            raise _IllegalParameterError(
                f'Node at index {i} is not a structure')
        if type(n.get('id')) != str:
            raise _IllegalParameterError(
                f'Node at index {i} must have an id key that maps to a string')
        try:
            type_ = _SubSampleType(n.get('type'))
        except ValueError:
            raise _IllegalParameterError(
                f'Node at index {i} has an invalid sample type: {n.get("type")}'
            )
        if n.get('parent') and type(n.get('parent')) != str:
            raise _IllegalParameterError(
                f'Node at index {i} has a parent entry that is not a string')
        mc = _check_meta(n.get('meta_controlled'), i, 'controlled metadata')
        mu = _check_meta(n.get('meta_user'), i, 'user metadata')
        try:
            nodes.append(
                _SampleNode(n.get('id'), type_, n.get('parent'), mc, mu))
            # already checked for the missing param error above, for id
        except _IllegalParameterError as e:
            raise _IllegalParameterError(f'Error for node at index {i}: ' +
                                         _cast(str, e.message)) from e

    id_ = get_id_from_object(s)

    pv = params.get('prior_version')
    if pv is not None and type(pv) != int:
        raise _IllegalParameterError(
            'prior_version must be an integer if supplied')
    s = Sample(nodes, s.get('name'))
    return (s, id_, pv)
コード例 #12
0
    def __init__(self,
                 owner: UserID,
                 lastupdate: datetime.datetime,
                 admin: Sequence[UserID] = None,
                 write: Sequence[UserID] = None,
                 read: Sequence[UserID] = None,
                 public_read: bool = False):
        '''
        Create the ACLs.

        :param owner: the owner username.
        :param lastupdate: the last time the ACLs were updated.
        :param admin: the list of admin usernames.
        :param write: the list of usernames with write privileges.
        :param read: the list of usernames with read privileges.
        :param public_read: a boolean designating whether the sample is publically readable.
            None is considered false.
        :raises IllegalParameterError: If a user appears in more than one ACL
        '''
        self.owner = _not_falsy(owner, 'owner')
        self.lastupdate = _check_timestamp(lastupdate, 'lastupdate')
        super().__init__(admin, write, read, public_read)
        all_ = (self.admin, self.write, self.read)
        for i in range(len(all_)):
            if self.owner in all_[i]:
                raise _IllegalParameterError(
                    'The owner cannot be in any other ACL')
コード例 #13
0
    def prefix_key_metadata(
            self,
            keys: List[str],
            exact_match: bool = True) -> Dict[str, Dict[str, PrimitiveType]]:
        '''
        Get any metdata associated with the specified prefix keys.

        :param keys: The keys to query.
        :param exact_match: If False, any metadata keys that match a prefix of the given keys
            will be included. If True, the given keys must match metadata keys exactly.
        :returns: A mapping of keys to their metadata.
        :raises IllegalParameterError: if one of the provided keys does not exist in this
            validator.
        '''
        if exact_match:
            return self._key_metadata(keys, self._prefix_vals_meta, 'prefix ')
        else:
            if keys is None:
                raise ValueError('keys cannot be None')
            ret = {}
            for k in keys:
                if not self._prefix_vals.shortest_prefix(k):
                    raise _IllegalParameterError(
                        f'No prefix metadata keys matching key {k}')
                for p in self._prefix_vals.prefixes(k):
                    ret[p.key] = self._prefix_vals_meta[p.key]
            return ret
コード例 #14
0
ファイル: workspace.py プロジェクト: jsfillman/sample_service
 def _check_upa(self, upa):
     upastr = upa.split('/')
     if len(upastr) != 3:
         raise _IllegalParameterError(f'{upa} is not a valid UPA')
     return (self._get_ws_num(upastr[0],
                              upa), self._get_ws_num(upastr[1], upa),
             self._get_ws_num(upastr[2], upa))
コード例 #15
0
ファイル: workspace.py プロジェクト: jsfillman/sample_service
    def __init__(self,
                 upa: str = None,
                 wsid: int = None,
                 objid: int = None,
                 version: int = None):
        '''
        Create the UPA. Requires either the upa parameter or all of the wsid, objid, and version
        parameters. If upa is supplied the other arguments are ignored.

        :param upa: The UPA as a string.
        :param wsid: The workspace ID.
        :param objid: The object ID.
        :param version: The object version.
        :raises IllegalParameterError: if the UPA is invalid.
        '''
        if upa:
            self.wsid, self.objid, self.version = self._check_upa(upa)
        else:
            for num, name in ((wsid, 'workspace ID'), (objid, 'object ID'),
                              (version, 'object version')):
                if not num or num < 1:
                    raise _IllegalParameterError(f'Illegal {name}: {num}')
            self.wsid = wsid
            self.objid = objid
            self.version = version
コード例 #16
0
ファイル: samples.py プロジェクト: jsfillman/sample_service
 def get_sample(self,
                id_: UUID,
                user: UserID,
                version: int = None,
                as_admin: bool = False) -> SavedSample:
     '''
     Get a sample.
     :param id_: the ID of the sample.
     :param user: the username of the user getting the sample.
     :param version: The version of the sample to retrieve. Defaults to the latest version.
     :param as_admin: Skip ACL checks.
     :returns: the sample.
     :raises IllegalParameterError: if version is supplied and is < 1
     :raises UnauthorizedError: if the user does not have read permission for the sample.
     :raises NoSuchSampleError: if the sample does not exist.
     :raises NoSuchSampleVersionError: if the sample version does not exist.
     :raises SampleStorageError: if the sample could not be retrieved.
     '''
     if version is not None and version < 1:
         raise _IllegalParameterError('Version must be > 0')
     self._check_perms(_not_falsy(id_, 'id_'),
                       _not_falsy(user, 'user'),
                       _SampleAccessType.READ,
                       as_admin=as_admin)
     return self._storage.get_sample(id_, version)
コード例 #17
0
    def __init__(self,
                 admin: Sequence[UserID] = None,
                 write: Sequence[UserID] = None,
                 read: Sequence[UserID] = None,
                 remove: Sequence[UserID] = None,
                 public_read: Optional[bool] = None,
                 at_least: bool = False):
        '''
        Create the ACLs.

        :param admin: the list of usernames to be granted admin privileges.
        :param write: the list of usernames to be granted write privileges.
        :param read: the list of usernames to be granted read privileges.
        :param remove: the list of usernames to have all privileges removed.
        :param public_read: a boolean designating whether the sample is publically readable.
            None signifies no change.
        :ivar at_least: True signifies that the provided user's permissions should not be
            downgraded if they are greater than the permission in the delta ACL. If False, the
            user's permission will be set to exactly the permission in the delta ACL. None is
            treated as False.
        :raises IllegalParameterError: If a user appears in more than one ACL
        '''
        self.admin = _to_tuple(admin, 'admin')
        self.write = _to_tuple(write, 'write')
        self.read = _to_tuple(read, 'read')
        self.remove = _to_tuple(remove, 'remove')
        self.public_read = public_read
        self.at_least = bool(at_least)  # handle None
        _check_acl_duplicates(self.admin, self.write, self.read)
        all_ = set(self.admin + self.write + self.read)
        for r in self.remove:
            if r in all_:
                raise _IllegalParameterError(
                    'Users in the remove list cannot be in any other ACL')
コード例 #18
0
 def _key_metadata(self, keys, meta, name_):
     if keys is None:
         raise ValueError('keys cannot be None')
     ret = {}
     for k in keys:
         if k not in meta:
             raise _IllegalParameterError(f'No such {name_}metadata key: {k}')
         ret[k] = meta[k]
     return ret
コード例 #19
0
def _check_string_int(params: Dict[str, Any], key: str, required=False) -> Optional[str]:
    v = params.get(key)
    if v is None:
        if required:
            raise _MissingParameterError(key)
        else:
            return None
    if type(v) != str:
        raise _IllegalParameterError(f'{key} key is not a string as required')
    return _cast(str, v)
コード例 #20
0
    def __init__(self,
                 admin: Sequence[UserID] = None,
                 write: Sequence[UserID] = None,
                 read: Sequence[UserID] = None):
        '''
        Create the ACLs.

        :param admin: the list of admin usernames.
        :param write: the list of usernames with write privileges.
        :param read: the list of usernames with read privileges.
        :raises IllegalParameterError: if a user appears in more than one ACL.
        '''
        self.admin = self._to_tuple(admin, 'admin')
        self.write = self._to_tuple(write, 'write')
        self.read = self._to_tuple(read, 'read')
        for u in self.admin:
            if u in self.write or u in self.read:
                raise _IllegalParameterError(f'User {u} appears in two ACLs')
        for u in self.write:
            if u in self.read:
                raise _IllegalParameterError(f'User {u} appears in two ACLs')
コード例 #21
0
    def save_sample(self,
                    sample: Sample,
                    user: UserID,
                    id_: UUID = None,
                    prior_version: Optional[int] = None,
                    as_admin: bool = False) -> Tuple[UUID, int]:
        '''
        Save a sample.

        :param sample: the sample to save.
        :param user: the username of the user saving the sample.
        :param id_: if the sample is a new version of a sample, the ID of the sample which will
            get a new version.
        :param prior_version: if id_ is included, specifying prior_version will ensure that the new
            sample is saved with version prior_version + 1 or not at all.
        :param as_admin: skip ACL checks for new versions.
        :returns a tuple of the sample ID and version.
        :raises IllegalParameterError: if the prior version is < 1
        :raises UnauthorizedError: if the user does not have write permission to the sample when
            saving a new version.
        :raises NoSuchSampleError: if the sample does not exist when saving a new version.
        :raises SampleStorageError: if the sample could not be retrieved when saving a new version
            or if the sample fails to save.
        :raises ConcurrencyError: if the sample's version is not equal to prior_version.
        '''
        _not_falsy(sample, 'sample')
        _not_falsy(user, 'user')
        self._validate_metadata(sample)
        if id_:
            if prior_version is not None and prior_version < 1:
                raise _IllegalParameterError('Prior version must be > 0')
            self._check_perms(id_,
                              user,
                              _SampleAccessType.WRITE,
                              as_admin=as_admin)
            swid = SavedSample(id_, user, list(sample.nodes), self._now(),
                               sample.name)
            ver = self._storage.save_sample_version(swid, prior_version)
        else:
            id_ = self._uuid_gen()
            swid = SavedSample(id_, user, list(sample.nodes), self._now(),
                               sample.name)
            # don't bother checking output since we created uuid
            self._storage.save_sample(swid)
            ver = 1
        if self._kafka:
            self._kafka.notify_new_sample_version(id_, ver)
        return (id_, ver)
コード例 #22
0
def validate_sample_id(id_, name=None):
    '''
    Given a string, validate the sample ID.

    :param id_: the sample's ID.
    :param name: the name of the ID to use in an exception, defaulting to the key.
    :returns: the ID, if it is a valid UUID
    :raises IllegalParameterError: if the ID is provided but is invalid.
    '''
    err = _IllegalParameterError(f'{name} {id_} must be a UUID string')
    if type(id_) != str:
        raise err
    try:
        return UUID(id_)
    except ValueError as _:  # noqa F841
        raise err
コード例 #23
0
def acls_from_dict(d: Dict[str, Any]) -> SampleACLOwnerless:
    '''
    Given a dict, create a SampleACLOwnerless object from the contents of the acls key.

    :param params: The dict containing the ACLS.
    :returns: the ACLs.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    _not_falsy(d, 'd')
    if d.get('acls') is None or type(d['acls']) != dict:
        raise _IllegalParameterError(
            'ACLs must be supplied in the acls key and must be a mapping')
    acls = d['acls']

    return SampleACLOwnerless(_get_acl(acls, 'admin'), _get_acl(acls, 'write'),
                              _get_acl(acls, 'read'))
コード例 #24
0
def get_user_from_object(params: Dict[str, Any], key: str) -> Optional[UserID]:
    '''
    Get a user ID from a key in an object.

    :param params: the dict containing the user.
    :param key: the key in the dict where the value is the username.
    :returns: the user ID or None if the user is not present.
    :raises IllegalParameterError: if the user if invalid.
    '''
    _check_params(params)
    u = params.get(_cast(str, _check_string(key, 'key')))
    if u is None:
        return None
    if type(u) is not str:
        raise _IllegalParameterError(f'{key} must be a string if present')
    else:
        return UserID(u)
コード例 #25
0
def get_datetime_from_epochmilliseconds_in_object(
        params: Dict[str, Any], key: str) -> Optional[datetime.datetime]:
    '''
    Get a non-naive datetime from a field in an object where the value is epoch milliseconds.

    :param params: the object.
    :param key: the key in the object for which the value is an epoch millisecond timestamp.
    :returns: the datatime or None if none was provided.
    :raises IllegalParameterError: if the timestamp is illegal.
    '''
    t = _check_params(params).get(key)
    if t is None:
        return None
    if type(t) != int:
        raise _IllegalParameterError(
            f"key '{key}' value of '{t}' is not a valid epoch millisecond timestamp")
    return datetime.datetime.fromtimestamp(t / 1000, tz=datetime.timezone.utc)
コード例 #26
0
def get_version_from_object(params: Dict[str, Any], required: bool = False) -> Optional[int]:
    '''
    Given a dict, get a sample version from the dict if it exists, using the key 'version'.

    :param params: the unmarshalled JSON recieved from the API as part of the API call.
    :param required: if True, throw and exception if the version is not supplied.
    :returns: the version or None if no version was provided.
    :raises MissingParameterError: if the version is required and not present.
    :raises IllegalParameterError: if the version is not an integer or < 1.
    '''
    _check_params(params)
    ver = params.get('version')
    if ver is None and required:
        raise _MissingParameterError('version')
    if ver is not None and (type(ver) != int or ver < 1):
        raise _IllegalParameterError(f'Illegal version argument: {ver}')
    return ver
コード例 #27
0
def _check_source_meta(m, index) -> List[_SourceMetadata]:
    if not m:
        return []
    if type(m) != list:
        raise _IllegalParameterError(
            f"Node at index {index}'s source metadata must be a list")
    ret = []
    for i, sm in enumerate(m):
        errprefix = f"Node at index {index}'s source metadata has an entry at index {i}"
        if type(sm) != dict:
            raise _IllegalParameterError(f'{errprefix} that is not a dict')
        if type(sm.get('key')) != str:
            raise _IllegalParameterError(
                f'{errprefix} where the required key field is not a string')
        # there's some duplicate code here, but I find getting the error messages right
        # is too difficult when DRYing up code like this. They're kind of sucky as is
        if type(sm.get('skey')) != str:
            raise _IllegalParameterError(
                f'{errprefix} where the required skey field is not a string')
        if type(sm.get('svalue')) != dict:
            raise _IllegalParameterError(
                f'{errprefix} where the required svalue field is not a mapping'
            )
        for vk in sm['svalue']:
            if type(vk) != str:
                raise _IllegalParameterError(
                    f'{errprefix} with a value mapping key that is not a string'
                )
            v = sm['svalue'][vk]
            if type(v) != str and type(v) != int and type(v) != float and type(
                    v) != bool:
                raise _IllegalParameterError(
                    f'{errprefix} with a value in the value mapping under key {vk} '
                    + 'that is not a primitive type')
        try:
            ret.append(_SourceMetadata(sm['key'], sm['skey'], sm['svalue']))
        except _IllegalParameterError as e:
            raise _IllegalParameterError(
                f"Node at index {index}'s source metadata has an error at index {i}: "
                + f'{e.message}') from e
    return ret
コード例 #28
0
def acl_delta_from_dict(d: Dict[str, Any]) -> SampleACLDelta:
    '''
    Given a dict, create a SampleACLDelta object from the contents of the dict.

    :param params: The dict containing the ACL delta.
    :returns: the ACL delta.
    :raises IllegalParameterError: if any of the arguments are illegal.
    '''
    # since we're translating from SDK server data structures, we assume this is a dict
    incpub = d.get('public_read')
    if incpub is None or incpub == 0:
        pub = None
    elif type(incpub) != int:
        raise _IllegalParameterError(
            'public_read must be an integer if present')
    else:
        pub = incpub > 0

    return SampleACLDelta(_get_acl(d, 'admin'), _get_acl(d, 'write'),
                          _get_acl(d, 'read'), _get_acl(d, 'remove'), pub,
                          bool(d.get('at_least')))
コード例 #29
0
    def __init__(self,
                 owner: UserID,
                 admin: Sequence[UserID] = None,
                 write: Sequence[UserID] = None,
                 read: Sequence[UserID] = None):
        '''
        Create the ACLs.

        :param owner: the owner username.
        :param admin: the list of admin usernames.
        :param write: the list of usernames with write privileges.
        :param read: the list of usernames with read privileges.
        :raises IllegalParameterError: If a user appears in more than one ACL
        '''
        self.owner = _not_falsy(owner, 'owner')
        super().__init__(admin, write, read)
        all_ = (self.admin, self.write, self.read)
        for i in range(len(all_)):
            if self.owner in all_[i]:
                raise _IllegalParameterError(
                    'The owner cannot be in any other ACL')
コード例 #30
0
ファイル: workspace.py プロジェクト: jsfillman/sample_service
    def has_permission(self,
                       user: UserID,
                       perm: WorkspaceAccessType,
                       workspace_id: int = None,
                       upa: UPA = None):
        '''
        Check if a user can access a workspace resource. Exactly one of workspace_id or upa must
        be supplied - if both are supplied workspace_id takes precedence.

        Beware - passing a NONE permission will not throw errors unless the object or workspace
        does not exist.

        The user is not checked for existence.

        :param user: The user's user name.
        :param perm: The requested permission
        :param workspace_id: The ID of the workspace.
        :param upa: a workspace service UPA.
        :raises IllegalParameterError: if the wsid is illegal.
        :raises UnauthorizedError: if the user doesn't have the requested permission.
        :raises NoSuchWorkspaceDataError: if the workspace or UPA doesn't exist.
        '''
        _not_falsy(user, 'user')
        _not_falsy(perm, 'perm')
        if workspace_id is not None:
            wsid = workspace_id
            name = 'workspace'
            target = str(workspace_id)
            upa = None
        elif upa:
            wsid = upa.wsid
            name = 'upa'
            target = str(upa)
        else:
            raise ValueError('Either an UPA or a workpace ID must be supplied')
        if wsid < 1:
            raise _IllegalParameterError(f'{wsid} is not a valid workspace ID')

        try:
            p = self._ws.administer({
                'command': 'getPermissionsMass',
                'params': {
                    'workspaces': [{
                        'id': wsid
                    }]
                }
            })
        except _ServerError as se:
            # this is pretty ugly, need error codes
            if 'No workspace' in se.args[0] or 'is deleted' in se.args[0]:
                raise _NoSuchWorkspaceDataError(se.args[0]) from se
            else:
                raise
        # could optimize a bit if NONE and upa but not worth the code complication most likely
        if (perm != WorkspaceAccessType.NONE
                and p['perms'][0].get(user.id) not in _PERM_TO_PERM_SET[perm]):
            raise _UnauthorizedError(
                f'User {user} cannot {_PERM_TO_PERM_TEXT[perm]} {name} {target}'
            )
        if upa:
            # Allow any server errors to percolate upwards
            # theoretically the workspace could've been deleted between the last call and this
            # one, but that'll just result in a different error and is extremely unlikely to
            # happen, so don't worry about it
            ret = self._ws.administer({
                'command': 'getObjectInfo',
                'params': {
                    'objects': [{
                        'ref': str(upa)
                    }],
                    'ignoreErrors': 1
                }
            })
            if not ret['infos'][0]:
                raise _NoSuchWorkspaceDataError(f'Object {upa} does not exist')