Beispiel #1
0
def validate_status_transition(af, from_status, to_status):
    if from_status == 'deleted':
        msg = _("Cannot change status if artifact is deleted.")
        raise exception.Forbidden(msg)
    if to_status == 'active':
        if from_status == 'drafted':
            for name, type_obj in af.fields.items():
                if type_obj.required_on_activate and getattr(af, name) is None:
                    msg = _("'%s' field value must be set before "
                            "activation.") % name
                    raise exception.Forbidden(msg)
    elif to_status == 'drafted':
        if from_status != 'drafted':
            msg = _("Cannot change status to 'drafted'") % from_status
            raise exception.Forbidden(msg)
    elif to_status == 'deactivated':
        if from_status not in ('active', 'deactivated'):
            msg = _("Cannot deactivate artifact if it's not active.")
            raise exception.Forbidden(msg)
    elif to_status == 'deleted':
        msg = _("Cannot delete artifact with PATCH requests. Use special "
                "API to do this.")
        raise exception.Forbidden(msg)
    else:
        msg = _("Unknown artifact status: %s.") % to_status
        raise exception.BadRequest(msg)
Beispiel #2
0
def validate_visibility_transition(af, from_visibility, to_visibility):
    if to_visibility == 'private':
        if from_visibility != 'private':
            msg = _("Cannot make artifact private again.")
            raise exception.Forbidden()
    elif to_visibility == 'public':
        if af.status != 'active':
            msg = _("Cannot change visibility to 'public' if artifact"
                    " is not active.")
            raise exception.Forbidden(msg)
    else:
        msg = _("Unknown artifact visibility: %s.") % to_visibility
        raise exception.BadRequest(msg)
Beispiel #3
0
    def delete_external_blob(self,
                             context,
                             type_name,
                             artifact_id,
                             field_name,
                             blob_key=None):
        """Delete artifact blob with external location.

        :param context: user context
        :param type_name: name of artifact type
        :param artifact_id: id of artifact with the blob to delete
        :param field_name: name of blob or blob dict field
        :param blob_key: if field_name is blob dict it specifies key
         in this dictionary
        """
        af = self._show_artifact(context, type_name, artifact_id)
        action_name = 'artifact:delete_blob'
        policy.authorize(action_name, af.to_dict(), context)

        blob_name = self._generate_blob_name(field_name, blob_key)

        blob = self._get_blob_info(af, field_name, blob_key)
        if blob is None:
            msg = _("Blob %s wasn't found for artifact") % blob_name
            raise exception.NotFound(message=msg)
        if not blob['external']:
            msg = _("Blob %s is not external") % blob_name
            raise exception.Forbidden(message=msg)

        af = self._save_blob_info(context, af, field_name, blob_key, None)

        Notifier.notify(context, action_name, af)
        return af.to_dict()
Beispiel #4
0
    def _show_artifact(ctx,
                       type_name,
                       artifact_id,
                       read_only=False,
                       get_any_artifact=False):
        """Return artifact requested by user.

        Check access permissions and policies.

        :param ctx: user context
        :param type_name: artifact type name
        :param artifact_id: id of the artifact to be updated
        :param read_only: flag, if set to True only read access is checked,
         if False then engine checks if artifact can be modified by the user
        :param get_any_artifact: flag, if set to True will get artifact from
        any realm
        """
        artifact_type = registry.ArtifactRegistry.get_artifact_type(type_name)
        # only artifact is available for class users
        af = artifact_type.show(ctx, artifact_id, get_any_artifact)
        if not read_only and not get_any_artifact:
            if not ctx.is_admin and ctx.tenant != af.owner or ctx.read_only:
                raise exception.Forbidden()
            LOG.debug("Artifact %s acquired for read-write access",
                      artifact_id)
        else:
            LOG.debug("Artifact %s acquired for read-only access", artifact_id)

        return af
Beispiel #5
0
def verify_artifact_count(context, type_name):
    """Verify if user can upload data based on his quota limits.

    :param context: user context
    :param type_name: name of artifact type
    """
    global_limit = CONF.max_artifact_number
    type_limit = getattr(CONF,
                         'artifact_type:' + type_name).max_artifact_number

    # update limits if they were reassigned for project
    project_id = context.project_id
    quotas = list_quotas(project_id).get(project_id, {})
    if 'max_artifact_number' in quotas:
        global_limit = quotas['max_artifact_number']
    if 'max_artifact_number:' + type_name in quotas:
        type_limit = quotas['max_artifact_number:' + type_name]

    session = api.get_session()

    if global_limit != -1:
        # the whole amount of created artifacts
        whole_number = api.count_artifact_number(context, session)

        if whole_number >= global_limit:
            msg = _("Can't create artifact because of global quota "
                    "limit is %(global_limit)d artifacts. "
                    "You have %(whole_number)d artifact(s).") % {
                        'global_limit': global_limit,
                        'whole_number': whole_number
                    }
            raise exception.Forbidden(msg)

    if type_limit != -1:
        # the amount of artifacts for specific type
        type_number = api.count_artifact_number(context, session, type_name)

        if type_number >= type_limit:
            msg = _("Can't create artifact because of quota limit for "
                    "artifact type '%(type_name)s' is %(type_limit)d "
                    "artifacts. You have %(type_number)d artifact(s) "
                    "of this type.") % {
                        'type_name': type_name,
                        'type_limit': type_limit,
                        'type_number': type_number
                    }
            raise exception.Forbidden(msg)
Beispiel #6
0
def validate_change_allowed(af, field_name):
    """Validate if fields can be set for the artifact."""
    if field_name not in af.fields:
        msg = _("Cannot add new field '%s' to artifact.") % field_name
        raise exception.BadRequest(msg)
    if af.status not in ('active', 'drafted'):
        msg = _("Forbidden to change fields "
                "if artifact is not active or drafted.")
        raise exception.Forbidden(message=msg)
    if af.fields[field_name].system is True:
        msg = _("Forbidden to specify system field %s. It is not "
                "available for modifying by users.") % field_name
        raise exception.Forbidden(msg)
    if af.status == 'active' and not af.fields[field_name].mutable:
        msg = (_("Forbidden to change field '%s' after activation.")
               % field_name)
        raise exception.Forbidden(message=msg)
Beispiel #7
0
        def _check_read_write_access(ctx, af):
            """Check if artifact can be modified by user

            :param ctx: user context
            :param af: artifact definition
            :raise Forbidden if access is not allowed
            """
            if not ctx.is_admin and ctx.tenant != af.owner or ctx.read_only:
                raise exception.Forbidden()
Beispiel #8
0
        def _check_read_only_access(ctx, af):
            """Check if user has read only access to artifact

            :param ctx: user context
            :param af: artifact definition
            :raise Forbidden if access is not allowed
            """
            private = af.visibility != 'public'
            if (private and not ctx.is_admin and ctx.tenant != af.owner):
                # TODO(kairat): check artifact sharing here
                raise exception.Forbidden()
Beispiel #9
0
    def authenticate(self, access_token, realm_name):
        info = None
        if self.mcclient:
            info = self.mcclient.get(access_token)

        if info is None and CONF.keycloak_oidc.user_info_endpoint_url:

            url = self.url_template % realm_name

            verify = None
            if urllib.parse.urlparse(url).scheme == "https":
                verify = False if self.insecure else self.cafile

            cert = (self.certfile, self.keyfile) \
                if self.certfile and self.keyfile else None

            try:
                resp = requests.get(
                    url,
                    headers={"Authorization": "Bearer %s" % access_token},
                    verify=verify,
                    cert=cert
                )
            except requests.ConnectionError:
                msg = _("Can't connect to keycloak server with address '%s'."
                        ) % CONF.keycloak_oidc.auth_url
                LOG.error(msg)
                raise exception.GlareException(message=msg)

            if resp.status_code == 400:
                raise exception.BadRequest(message=resp.text)
            if resp.status_code == 401:
                LOG.warning("HTTP response from OIDC provider:"
                            " [%s] with WWW-Authenticate: [%s]",
                            pprint.pformat(resp.text),
                            resp.headers.get("WWW-Authenticate"))
                raise exception.Unauthorized(message=resp.text)
            if resp.status_code == 403:
                raise exception.Forbidden(message=resp.text)
            elif resp.status_code > 400:
                raise exception.GlareException(message=resp.text)

            if self.mcclient:
                self.mcclient.set(access_token, resp.json(),
                                  time=CONF.keycloak_oidc.token_cache_time)
            info = resp.json()

        LOG.debug("HTTP response from OIDC provider: %s",
                  pprint.pformat(info))

        return info
Beispiel #10
0
    def create(self, req, type_name, values):
        """Create artifact record in Glare.

        :param req: user request
        :param type_name: artifact type name
        :param values: dict with artifact fields
        :return: definition of created artifact
        """
        if req.context.project_id is None or req.context.read_only:
            msg = _("It's forbidden to anonymous users to create artifacts.")
            raise exc.Forbidden(msg)
        if not values.get('name'):
            msg = _("Name must be specified at creation.")
            raise exc.BadRequest(msg)
        for field in ('visibility', 'status', 'display_type_name'):
            if field in values:
                msg = _("%s is not allowed in a request at creation.") % field
                raise exc.BadRequest(msg)
        return self.engine.create(req.context, type_name, values)
Beispiel #11
0
 def update_blob(cls, context, af_id, field_name, values):
     raise exception.Forbidden("This type is read only.")
Beispiel #12
0
 def delete(cls, context, af):
     raise exception.Forbidden("This type is read only.")
Beispiel #13
0
 def save(self, context):
     raise exception.Forbidden("This type is read only.")
Beispiel #14
0
 def create(cls, context):
     raise exception.Forbidden("This type is read only.")
Beispiel #15
0
    def download_blob(self,
                      context,
                      type_name,
                      artifact_id,
                      field_name,
                      blob_key=None):
        """Download binary data from Glare Artifact.

        :param context: user context
        :param type_name: name of artifact type
        :param artifact_id: id of the artifact to be updated
        :param field_name: name of blob or blob dict field
        :param blob_key: if field_name is blob dict it specifies key
         in this dict
        :return: file iterator for requested file
        """
        download_from_any_artifact = False
        if policy.authorize("artifact:download_from_any_artifact", {},
                            context,
                            do_raise=False):
            download_from_any_artifact = True

        af = self._show_artifact(context,
                                 type_name,
                                 artifact_id,
                                 read_only=True,
                                 get_any_artifact=download_from_any_artifact)

        if not download_from_any_artifact:
            policy.authorize("artifact:download", af.to_dict(), context)

        blob_name = self._generate_blob_name(field_name, blob_key)

        if af.status == 'deleted':
            msg = _("Cannot download data when artifact is deleted")
            raise exception.Forbidden(message=msg)

        blob = self._get_blob_info(af, field_name, blob_key)
        if blob is None:
            msg = _("No data found for blob %s") % blob_name
            raise exception.NotFound(message=msg)
        if blob['status'] != 'active':
            msg = _("%s is not ready for download") % blob_name
            raise exception.Conflict(message=msg)

        af.pre_download_hook(context, af, field_name, blob_key)

        meta = {
            'md5': blob.get('md5'),
            'sha1': blob.get('sha1'),
            'sha256': blob.get('sha256'),
            'external': blob.get('external')
        }
        if blob['external']:
            data = {'url': blob['url']}
        else:
            data = store_api.load_from_store(uri=blob['url'], context=context)
            meta['size'] = blob.get('size')
            meta['content_type'] = blob.get('content_type')

        try:
            # call download hook in the end
            data = af.post_download_hook(context, af, field_name, blob_key,
                                         data)
        except exception.GlareException:
            raise
        except Exception as e:
            raise exception.BadRequest(message=str(e))

        return data, meta
Beispiel #16
0
    def add_blob_location(self,
                          context,
                          type_name,
                          artifact_id,
                          field_name,
                          location,
                          blob_meta,
                          blob_key=None):
        """Add external/internal location to blob.

        :param context: user context
        :param type_name: name of artifact type
        :param artifact_id: id of the artifact to be updated
        :param field_name: name of blob or blob dict field
        :param location: blob url
        :param blob_meta: dictionary containing blob metadata like md5 checksum
        :param blob_key: if field_name is blob dict it specifies key
         in this dict
        :return: dict representation of updated artifact
        """
        blob_name = self._generate_blob_name(field_name, blob_key)

        location_type = blob_meta.pop('location_type', 'external')

        if location_type == 'external':
            action_name = 'artifact:set_location'
        elif location_type == 'internal':
            scheme = urlparse.urlparse(location).scheme
            if scheme in store_api.RESTRICTED_URI_SCHEMES:
                msg = _("Forbidden to set internal locations with "
                        "scheme '%s'") % scheme
                raise exception.Forbidden(msg)
            if scheme not in store_api.get_known_schemes():
                msg = _("Unknown scheme '%s'") % scheme
                raise exception.BadRequest(msg)
            action_name = 'artifact:set_internal_location'
        else:
            msg = _("Invalid location type: %s") % location_type
            raise exception.BadRequest(msg)

        blob = {
            'url': location,
            'size': None,
            'md5': blob_meta.get("md5"),
            'sha1': blob_meta.get("sha1"),
            'id': uuidutils.generate_uuid(),
            'sha256': blob_meta.get("sha256"),
            'status': 'active',
            'external': location_type == 'external',
            'content_type': None
        }

        lock_key = "%s:%s" % (type_name, artifact_id)
        with self.lock_engine.acquire(context, lock_key):
            af = self._show_artifact(context, type_name, artifact_id)
            policy.authorize(action_name, af.to_dict(), context)
            if self._get_blob_info(af, field_name, blob_key):
                msg = _("Blob %(blob)s already exists for artifact "
                        "%(af)s") % {
                            'blob': field_name,
                            'af': af.id
                        }
                raise exception.Conflict(message=msg)
            utils.validate_change_allowed(af, field_name)
            af.pre_add_location_hook(context, af, field_name, location,
                                     blob_key)
            af = self._save_blob_info(context, af, field_name, blob_key, blob)

        LOG.info(
            "External location %(location)s has been created "
            "successfully for artifact %(artifact)s blob %(blob)s", {
                'location': location,
                'artifact': af.id,
                'blob': blob_name
            })

        af.post_add_location_hook(context, af, field_name, blob_key)
        Notifier.notify(context, action_name, af)
        return af.to_dict()