Ejemplo n.º 1
0
    def expire_data_link(self,
                         user: UserID,
                         duid: DataUnitID,
                         as_admin: bool = False) -> None:
        '''
        Expire a data link, ensuring that it will not show up in link queries without an effective
        timestamp in the past.
        The user must have admin access to the sample and write access to the data. The data may
        be deleted.

        :param user: the user expiring the link.
        :param duid: the data unit ID for the extant link.
        :param as_admin: allow link expiration to proceed if user does not have
            appropriate permissions.
        :raises UnauthorizedError: if the user does not have acceptable permissions.
        :raises NoSuchWorkspaceDataError: if the workspace doesn't exist.
        :raises NoSuchLinkError: if there is no link from the data unit.
        '''
        _not_falsy(user, 'user')
        _not_falsy(duid, 'duid')
        # allow expiring links for deleted objects. It should be impossible to have a link
        # for an object that has never existed.
        # TODO DOCUMENT What about deleted workspaces? Links should be inaccessible as is
        wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.WRITE
        self._ws.has_permission(user, wsperm, workspace_id=duid.upa.wsid)
        link = self._storage.get_data_link(duid=duid)
        # was concerned about exposing the sample ID, but if the user has write access to the
        # UPA then they can get the link with the sample ID, so don't worry about it.
        self._check_perms(link.sample_node_address.sampleid,
                          user,
                          _SampleAccessType.ADMIN,
                          as_admin=as_admin)
        self._storage.expire_data_link(self._now(), user, duid=duid)
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
 def __init__(self,
              id_: UUID,
              user: UserID,
              nodes: List[SampleNode],
              savetime: datetime.datetime,
              name: Optional[str] = None,
              version: Optional[int] = None):
     '''
     Create the sample.
     :param id_: The ID of the sample.
     :param user: The user who saved the sample.
     :param nodes: The tree nodes in the sample. BIOLOGICAL_REPLICATES must come first in
         the list, and parents must come before children in the list.
     :param savetime: The time the sample was saved. Cannot be a naive datetime.
     :param name: The name of the sample. Cannot contain control characters or be longer than
         255 characters.
     :param version: The version of the sample, or None if unknown.
     :raise MissingParameterError: if no nodes are provided.
     :raises IllegalParameterError: if the name is too long or contains illegal characters,
         the first node in the list is not a BIOLOGICAL_REPLICATE, all the BIOLOGICAL_REPLICATES
         are not at the start of this list, node names are not unique, or parent nodes
         do not appear in the list prior to their children.
     '''
     # having None as a possible version doesn't sit well with me, but that means we need
     # yet another class, so...
     super().__init__(nodes, name)
     self.id = _not_falsy(id_, 'id_')
     self.user = _not_falsy(user, 'user')
     self.savetime = _check_timestamp(savetime, 'savetime')
     if version is not None and version < 1:
         raise ValueError('version must be > 0')
     self.version = version
Ejemplo n.º 4
0
    def get_sample_via_data(self, user: Optional[UserID], upa: UPA,
                            sample_address: SampleAddress) -> SavedSample:
        '''
        Given a workspace object, get a sample linked to that object. The user must have read
        permissions for the object, but not necessarily the sample. The link may be expired.

        :param user: The user requesting the sample or None if the user is anonymous.
        :param upa: the data from which the link to the sample originates.
        :param sample_address: the sample address.
        :returns: the linked sample.
        :raises UnauthorizedError: if the user cannot read the UPA.
        :raises NoSuchWorkspaceDataError: if the workspace object does not exist.
        :raises NoSuchLinkError: if there is no link from the object to the sample.
        :raises NoSuchSampleVersionError: if the sample version does not exist.
        '''
        # no admin mode needed - use get_links or get sample
        # may need to make this independent of the workspace. YAGNI.
        # handle ref path?
        _not_falsy(upa, 'upa')
        _not_falsy(sample_address, 'sample_address')
        # the order of these checks is important, check read first, then we know link & sample
        # access is ok
        self._ws.has_permission(user, _WorkspaceAccessType.READ, upa=upa)
        if not self._storage.has_data_link(upa, sample_address.sampleid):
            raise _NoSuchLinkError(
                f'There is no link from UPA {upa} to sample {sample_address.sampleid}'
            )
        # can't raise no sample error since a link exists
        return self._storage.get_sample(sample_address.sampleid,
                                        sample_address.version)
Ejemplo n.º 5
0
    def expire_data_link(self, user: UserID, duid: DataUnitID, as_admin: bool = False) -> None:
        '''
        Expire a data link, ensuring that it will not show up in link queries without an effective
        timestamp in the past.
        The user must have admin access to the sample and write access to the data. The data may
        be deleted.

        :param user: the user expiring the link.
        :param duid: the data unit ID for the extant link.
        :param as_admin: allow link expiration to proceed if user does not have
            appropriate permissions.
        :raises UnauthorizedError: if the user does not have acceptable permissions.
        :raises NoSuchWorkspaceDataError: if the workspace doesn't exist.
        :raises NoSuchLinkError: if there is no link from the data unit.
        '''
        _not_falsy(user, 'user')
        _not_falsy(duid, 'duid')
        # allow expiring links for deleted objects. It should be impossible to have a link
        # for an object that has never existed.
        wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.WRITE
        self._ws.has_permission(user, wsperm, workspace_id=duid.upa.wsid)
        link = self._storage.get_data_link(duid=duid)
        # was concerned about exposing the sample ID, but if the user has write access to the
        # UPA then they can get the link with the sample ID, so don't worry about it.
        self._check_perms(
            link.sample_node_address.sampleid, user, _SampleAccessType.ADMIN, as_admin=as_admin)
        # Use the ID here to prevent a race condition expiring a new link to a different sample
        # since the user may not have perms
        # There's a chance the link could be expired between db fetch and update, but that
        # takes millisecond precision and just means a funky error message occurs, so don't
        # worry about it.
        self._storage.expire_data_link(self._now(), user, id_=link.id)
        if self._kafka:
            self._kafka.notify_expired_link(link.id)
Ejemplo n.º 6
0
    def get_links_from_data(
            self,
            user: UserID,
            upa: UPA,
            timestamp: datetime.datetime = None,
            as_admin: bool = False
    ) -> Tuple[List[DataLink], datetime.datetime]:
        '''
        Get a set of data links originating from a workspace object at a particular time.

        :param user: the user requesting the links.
        :param upa: the data from which the links originate.
        :param timestamp: the timestamp during which the links should be active, defaulting to
            the current time.
        :param as_admin: allow link retrieval to proceed if user does not have
            appropriate permissions.
        :returns: a tuple consisting of a list of links and the timestamp used to query the links.
        :raises UnauthorizedError: if the user does not have read permission for the data.
        :raises NoSuchWorkspaceDataError: if the data does not exist.
        '''
        # may need to make this independent of the workspace. YAGNI.
        # handle ref path?
        _not_falsy(user, 'user')
        _not_falsy(upa, 'upa')
        timestamp = self._resolve_timestamp(timestamp)
        # NONE still checks that WS/obj exists. If it's deleted this method should fail
        wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.READ
        self._ws.has_permission(user, wsperm, upa=upa)
        return self._storage.get_links_from_data(upa, timestamp), timestamp
Ejemplo n.º 7
0
    def get_links_from_sample(
            self,
            user: UserID,
            sample: SampleAddress,
            timestamp: datetime.datetime = None,
            as_admin: bool = False
    ) -> Tuple[List[DataLink], datetime.datetime]:
        '''
        Get a set of data links originating from a sample at a particular time.

        :param user: the user requesting the links.
        :param sample: the sample from which the links originate.
        :param timestamp: the timestamp during which the links should be active, defaulting to
            the current time.
        :param as_admin: allow link retrieval to proceed if user does not have
            appropriate permissions.
        :returns: a tuple consisting of a list of links and the timestamp used to query the links.
        :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 NoSuchUserError: if the user does not exist.
        '''
        _not_falsy(user, 'user')
        _not_falsy(sample, 'sample')
        timestamp = self._resolve_timestamp(timestamp)
        self._check_perms(sample.sampleid,
                          user,
                          _SampleAccessType.READ,
                          as_admin=as_admin)
        wsids = None if as_admin else self._ws.get_user_workspaces(user)
        # TODO DATALINK what about deleted objects? Currently not handled
        return self._storage.get_links_from_sample(sample, wsids,
                                                   timestamp), timestamp
Ejemplo n.º 8
0
    def get_batch_links_from_sample_set(
            self,
            user: Optional[UserID],
            samples: List[SampleAddress],
            timestamp: datetime.datetime = None,
            as_admin: bool = False) -> Tuple[List[DataLink], datetime.datetime]:
        '''
        A batch version of get_links_from_sample. Gets a set of  data links originating
        from multiple samples in a given sampleset at a particular time.

        :param user: the user requesting the links or None if the user is anonymous.
        :param samples: the list of sample ids from which the links originate.
        :param timestamp: the timestamp during which the links should be active, defaulting to
            the current time.
        :param as_admin: allow link retrieval to proceed if user does not have
            appropriate permissions.
        :returns: a tuple consisting of a list of links and the timestamp used to query the links.
        :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 NoSuchUserError: if the user does not exist.
        '''
        _not_falsy(samples, 'samples')
        timestamp = self._resolve_timestamp(timestamp)
        wsids = None if as_admin else self._ws.get_user_workspaces(user)
        # checks for all sample acls in one query
        sampleids = [s.sampleid for s in samples]
        self._check_batch_perms(sampleids, user, _SampleAccessType.READ, as_admin=as_admin)
        return_links = self._storage.get_batch_links_from_samples(samples, wsids, timestamp)
        return return_links, timestamp
Ejemplo n.º 9
0
    def __init__(
            self,
            id_: uuid.UUID,
            duid: DataUnitID,
            sample_node_address: SampleNodeAddress,
            created: datetime.datetime,
            created_by: UserID,
            expired: datetime.datetime = None,
            expired_by: UserID = None):
        '''
        Create the link. If expired is provided expired_by must also be provided. If expired
        is falsy expired_by is ignored.

        :param id_: the link ID. This is generally expected to be unique per link.
        :param duid: the data ID.
        :param sample_node_address: the sample node address.
        :param created: the creation time for the link.
        :param created_by: the user that created the link.
        :param expired: the expiration time for the link or None if the link is not expired.
        :param expired_by: the user that expired the link or None if the link is not expired.
        '''
        # may need to make this non ws specific. YAGNI for now.
        self.id = _not_falsy(id_, 'id_')
        self.duid = _not_falsy(duid, 'duid')
        self.sample_node_address = _not_falsy(sample_node_address, 'sample_node_address')
        self.created = _check_timestamp(created, 'created')
        self.created_by = _not_falsy(created_by, 'created_by')
        self.expired = None
        self.expired_by = None
        if expired:
            self.expired = _check_timestamp(expired, 'expired')
            if expired < created:
                raise ValueError('link cannot expire before it is created')
            self.expired_by = _not_falsy(expired_by, 'expired_by')
Ejemplo n.º 10
0
    def validate_sample(self, sample: Sample):
        '''
        This method performs only the validation steps on a sample

        :param sample: the sample to validate
        '''
        _not_falsy(sample, 'sample')
        error_strings = self._validate_metadata(sample, return_error_strings=True)
        return error_strings
Ejemplo n.º 11
0
    def is_equivalent(self, link: DataLink):
        '''
        Check whether this link is equivalent to another link. Equivalent means that the
        DUID and sample node address are identical.

        :param link: The link to check.
        :returns: True if the links are equivalent, False otherwise.
        '''
        _not_falsy(link, 'link')
        return (self.duid, self.sample_node_address) == (link.duid, link.sample_node_address)
Ejemplo n.º 12
0
    def validate_sample(self, sample: Sample):
        '''
        This method performs only the validation steps on a sample

        :param sample: the sample to validate
        '''
        _not_falsy(sample, 'sample')
        error_detail = self._validate_metadata(sample, return_error_detail=True)
        for e in error_detail:
            e['sample_name'] = sample.name
        return error_detail
Ejemplo n.º 13
0
    def __init__(self,
                 auth_url: str,
                 auth_token: str,
                 full_admin_roles: List[str] = None,
                 read_admin_roles: List[str] = None,
                 cache_max_size: int = 10000,
                 cache_admin_expiration: int = 300,
                 cache_valid_expiration: int = 3600):
        '''
        Create the client.
        :param auth_url: The root url of the authentication service.
        :param auth_token: A valid token for the authentication service.
        :raises InvalidTokenError: if the token is invalid
        :param cache_max_size: the maximum size of the token -> admin and username -> validity
            caches.
        :param cache_admin_expiration: the default expiration time for the token -> admin cache in
            seconds. This time can be overridden by a user handler on a per token basis.
        :param cache_valid_expiration: the default expiration time for the  username ->
            validity cache. This time can be overridden by a user handler on a per user basis.
        '''
        self._url = _not_falsy(auth_url, 'auth_url')
        if not self._url.endswith('/'):
            self._url += '/'
        self._user_url = self._url + 'api/V2/users?list='
        self._me_url = self._url + 'api/V2/me'
        self._token = _not_falsy(auth_token, 'auth_token')
        self._full_roles = set(full_admin_roles) if full_admin_roles else set()
        self._read_roles = set(read_admin_roles) if read_admin_roles else set()
        self._cache_timer = time.time
        self._admin_cache = LRUCache(timer=self._cache_timer,
                                     maxsize=cache_max_size,
                                     ttl=cache_admin_expiration)
        self._valid_cache = LRUCache(timer=self._cache_timer,
                                     maxsize=cache_max_size,
                                     ttl=cache_valid_expiration)

        # Auth 0.4.1 needs to be deployed before this will work
        # r = requests.get(self._url, headers={'Accept': 'application/json'})
        # self._check_error(r)
        # if r.json().get('servicename') != 'Authentication Service':
        #     raise IOError(f'The service at {self._url} does not appear to be the KBase ' +
        #                   'Authentication Service')

        # could use the server time to adjust for clock skew, probably not worth the trouble

        # check token is valid
        r = requests.get(self._user_url,
                         headers={
                             'Accept': 'application/json',
                             'authorization': self._token
                         })
        self._check_error(r)
Ejemplo n.º 14
0
    def create_data_link(self,
                         user: UserID,
                         duid: DataUnitID,
                         sna: SampleNodeAddress,
                         update: bool = False,
                         as_admin: bool = False) -> DataLink:
        '''
        Create a link from a data unit to a sample. The user must have admin access to the sample,
        since linking data grants permissions: once linked, if a user
        has access to the data unit, the user also has access to the sample. The user must have
        write access to the data since adding a sample to the data effectively modifies the data,
        but doesn't grant any additional access.

        Each data unit can be linked to only one sample at a time. Expired links may exist to
        other samples.

        :param user: the user creating the link.
        :param duid: the data unit to link the the sample.
        :param sna: the sample node to link to the data unit.
        :param update: True to expire any extant link if it does not link to the provided sample.
            If False and a link from the data unit already exists, link creation will fail.
        :param as_admin: allow link creation to proceed if user does not have
            appropriate permissions.
        :returns: the new link.
        :raises UnauthorizedError: if the user does not have acceptable permissions.
        :raises NoSuchSampleError: if the sample does not exist.
        :raises NoSuchSampleVersionError: if the sample version does not exist.
        :raises NoSuchSampleNodeError: if the sample node does not exist.
        :raises NoSuchWorkspaceDataError: if the workspace or UPA doesn't exist.
        :raises DataLinkExistsError: if a link already exists from the data unit.
        :raises TooManyDataLinksError: if there are too many links from the sample version or
            the workspace object version.
        :raises SampleStorageError: if the sample could not be retrieved.
        '''
        _not_falsy(user, 'user')
        _not_falsy(duid, 'duid')
        self._check_perms(_not_falsy(sna, 'sna').sampleid,
                          user,
                          _SampleAccessType.ADMIN,
                          as_admin=as_admin)
        wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.WRITE
        self._ws.has_permission(user, wsperm, upa=duid.upa)
        dl = DataLink(self._uuid_gen(), duid, sna, self._now(), user)
        expired_id = self._storage.create_data_link(dl, update=update)
        if self._kafka:
            self._kafka.notify_new_link(dl.id)
            if expired_id:  # maybe make the notifier accept both notifications & send both?
                self._kafka.notify_expired_link(expired_id)
        return dl
Ejemplo n.º 15
0
    def is_admin(self, token: str) -> Tuple[AdminPermission, str]:
        '''
        Check whether a user is a service administrator.

        :param token: The user's token.
        :returns: A tuple consisting of an enum indicating the user's administration permissions,
          if any, and the username.
        '''
        # TODO ACL cache admin users
        # TODO CODE should regex the token to check for \n etc., but the SDK has already checked it
        _not_falsy(token, 'token')
        r = requests.get(self._me_url, headers={'Authorization': token})
        self._check_error(r)
        j = r.json()
        return self._get_role(j['customroles']), j['user']
Ejemplo n.º 16
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)
Ejemplo n.º 17
0
    def get_user_workspaces(self, user: UserID) -> List[int]:
        '''
        Get a list of IDs of workspaces a user can read, including public workspaces.

        :param user: The username of the user whose workspaces will be returned.
        :returns: A list of workspace IDs.
        :raises NoSuchUserError: if the user does not exist.
        '''
        # May also want write / admin / no public ws
        try:
            ids = self._ws.administer({
                'command': 'listWorkspaceIDs',
                'user': _not_falsy(user, 'user').id,
                'params': {
                    'perm': 'r',
                    'excludeGlobal': 0
                }
            })
        except _ServerError as se:
            # this is pretty ugly, need error codes
            if 'not a valid user' in se.args[0]:
                raise _NoSuchUserError(se.args[0]) from se
            else:
                raise
        return sorted(ids['workspaces'] + ids['pub'])
Ejemplo n.º 18
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'))
Ejemplo n.º 19
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')
Ejemplo n.º 20
0
def datetime_to_epochmilliseconds(d: datetime.datetime) -> int:
    '''
    Convert a datetime object to epoch milliseconds.

    :param d: The datetime.
    :returns: The date in epoch milliseconds.
    '''
    return round(_not_falsy(d, 'd').timestamp() * 1000)
Ejemplo n.º 21
0
    def is_update(self, update: SampleACLDelta) -> bool:
        '''
        Check if an acl delta update is actually an update or a noop for the sample.

        :param update: the update.
        :returns: True if the update would change the ACLs, False if not. The timestamp is not
            considered.
        :raises UnauthorizedError: if the update would affect the owner and update.at_least is
            not true, or if the owner is in the remove list regardless of at_least.
        '''
        _not_falsy(update, 'update')
        o = self.owner
        ownerchange = o in update.admin or o in update.write or o in update.read
        if (ownerchange and not update.at_least) or o in update.remove:
            raise _UnauthorizedError(
                f'ACLs for the sample owner {o.id} may not be modified by a delta update.'
            )

        rem = set(update.remove)
        admin = set(self.admin)
        write = set(self.write)
        read = set(self.read)

        # check if users are removed
        if not rem.isdisjoint(admin) or not rem.isdisjoint(
                write) or not rem.isdisjoint(read):
            return True

        # check if public read is changed
        if update.public_read is not None and update.public_read is not self.public_read:
            return True

        uadmin = set(update.admin)
        uwrite = set(update.write)
        uread = set(update.read)
        owner = set([o])

        # check if users' permission is changed
        if update.at_least:
            return (not uadmin.issubset(admin | owner)
                    or not uwrite.issubset(write | admin | owner)
                    or not uread.issubset(read | write | admin | owner))
        else:
            return (not uadmin.issubset(admin) or not uwrite.issubset(write)
                    or not uread.issubset(read))
Ejemplo n.º 22
0
    def notify_expired_link(self, link_id: UUID):
        """
        Send a notification that a link has been expired.

        :param link_id: the link ID.
        """
        self._send_message({
            self._EVENT_TYPE: self._EXPIRED_LINK,
            self._LINK_ID: str(_not_falsy(link_id, 'link_id'))
        })
Ejemplo n.º 23
0
 def get_samples(
     self,
     ids_: List[Dict[str, Any]],
     user: Optional[UserID],
     as_admin: bool = False) -> List[SavedSample]:
     '''
     '''
     for id_ in ids_:
         self._check_perms(_not_falsy(id_['id'], 'id_'), user, _SampleAccessType.READ, as_admin=as_admin)
     return self._storage.get_samples(ids_)
Ejemplo n.º 24
0
    def __init__(self, sampleid: UUID, version: int):
        '''
        Create the address.

        :param sampleid: The ID of the sample.
        :param version: The version of the sample.
        '''
        self.sampleid = _not_falsy(sampleid, 'sampleid')
        if version is None or version < 1:
            raise IllegalParameterError('version must be > 0')
        self.version = version
Ejemplo n.º 25
0
    def notify_sample_acl_change(self, sample_id: UUID):
        """
        Send a notification for a sample ACL change.

        :param sample_id: the sample ID.
        """
        self._send_message({
            self._EVENT_TYPE:
            self._ACL_CHANGE,
            self._SAMPLE_ID:
            str(_not_falsy(sample_id, 'sample_id'))
        })
Ejemplo n.º 26
0
    def get_data_link_admin(self, link_id: UUID) -> DataLink:
        '''
        This method is intended for admin use and should not be exposed in a public API.

        Get a link by its ID. The link may be expired.

        :param link_id: the link ID.
        :returns: the link.
        :raises NoSuchLinkError: if the link does not exist.
        '''
        # if we expose this to users need to add ACL checking. Don't see a use case ATM.
        return self._storage.get_data_link(_not_falsy(link_id, 'link_id'))
Ejemplo n.º 27
0
    def __init__(self, sample: SampleAddress, node: str):
        '''
        Create the address.

        :param sample: The sample address.
        :param node: The ID of the sample node.
        '''

        self.sampleid = _not_falsy(sample, 'sample').sampleid
        self.version = sample.version
        self.node = _cast(
            str, _check_string(node, 'node', max_len=_MAX_SAMPLE_NAME_LEN))
Ejemplo n.º 28
0
    def __init__(self, client: Workspace):
        '''
        Create the workspace class.

        Attempts to contact the endpoint of the workspace in administration mode and does not
        catch any exceptions encountered.

        :param client: An SDK workspace client with administrator read permissions.
        '''
        self._ws = _not_falsy(client, 'client')
        # check token is a valid admin token
        self._ws.administer({'command': 'listModRequests'})
Ejemplo n.º 29
0
    def __init__(
        self,
        storage: ArangoSampleStorage,
        user_lookup: KBaseUserLookup,  # make an interface? YAGNI
        metadata_validator: MetadataValidatorSet,
        workspace: WS,
        now: Callable[[], datetime.datetime] = lambda: datetime.datetime.now(
            tz=datetime.timezone.utc),
        uuid_gen: Callable[[], UUID] = lambda: _uuid.uuid4()):
        '''
        Create the class.

        :param storage: the storage system to use.
        :param user_lookup: a service to verify usernames are valid and exist.
        :param metadata_validator: A validator for metadata.
        '''
        # don't publicize these params
        # :param now: A callable that returns the current time. Primarily used for testing.
        # :param uuid_gen: A callable that returns a random UUID. Primarily used for testing.
        # extract an interface from ASS if needed.
        self._storage = _not_falsy(storage, 'storage')
        self._user_lookup = _not_falsy(user_lookup, 'user_lookup')
        self._metaval = _not_falsy(metadata_validator, 'metadata_validator')
        self._ws = _not_falsy(workspace, 'workspace')
        self._now = _not_falsy(now, 'now')
        self._uuid_gen = _not_falsy(uuid_gen, 'uuid_gen')
Ejemplo n.º 30
0
    def update_sample_acls(
            self,
            id_: UUID,
            user: UserID,
            update: SampleACLDelta,
            as_admin: bool = False) -> None:
        '''
        Completely replace a sample's ACLs.

        :param id_: the sample's ID.
        :param user: the user changing the ACLs.
        :param update: the ACL update. Note the update time is ignored. If the sample owner is
            in any of the lists in the update, the update will fail.
        :param as_admin: Skip ACL checks.
        :raises NoSuchUserError: if any of the users in the ACLs do not exist.
        :raises NoSuchSampleError: if the sample does not exist.
        :raises UnauthorizedError: if the user does not have admin permission for the sample or
            the request attempts to alter the owner.
        :raises SampleStorageError: if the sample could not be retrieved.
        '''
        # could make yet another ACL class that's a delta w/o an update time - probably not
        # worth it. If people get confused do it.
        _not_falsy(id_, 'id_')
        _not_falsy(user, 'user')
        _not_falsy(update, 'update')
        self._check_for_bad_users(_cast(List[UserID], []) + list(update.admin) +
                                  list(update.write) + list(update.read) + list(update.remove))

        self._check_perms(id_, user, _SampleAccessType.ADMIN, as_admin=as_admin)

        self._storage.update_sample_acls(id_, update, self._now())
        if self._kafka:
            self._kafka.notify_sample_acl_change(id_)