Ejemplo n.º 1
0
def get_karton_analysis(
        db_object: Object,
        root_uid: Optional[str] = None) -> Optional[KartonAnalysis]:
    # Includes 'karton' permission check
    metakeys = db_object.get_metakeys(as_dict=True)

    if "karton" not in metakeys:
        return None
    if not root_uid:
        # Metakeys are ordered from oldest to latest one
        root_uid = metakeys["karton"][-1]
    elif root_uid not in metakeys["karton"]:
        # root_uid must occur in attributes to get the analysis status
        return None

    karton_config = KartonConfig(config.karton.config_path)
    karton_backend = KartonBackend(karton_config)
    karton_state = KartonState(karton_backend)

    if root_uid not in karton_state.analyses:
        return None

    if karton_state.analyses[root_uid].is_done:
        return None

    return karton_state.analyses[root_uid]
Ejemplo n.º 2
0
    def get(self, identifier: str) -> Tuple[Dict[str, Any], int]:
        db_object = Object.access(identifier)
        if db_object is None:
            raise NotFound("Object not found")
        root_uid = request.args.get("root_uid")
        analysis = get_karton_analysis(db_object, root_uid)

        if analysis is None:
            return {"status": "finished"}, 200
        else:
            queues = {
                queue_name: {
                    "received_from": list(set(
                        task.headers["origin"] for task in queue.pending_tasks
                    )),
                    "status": list(set(
                        task.status.value for task in queue.pending_tasks
                    ))
                }
                for queue_name, queue in analysis.pending_queues.items()
            }
            return {
                "status": "running",
                "last_update": analysis.last_update,
                "processing_in": queues
            }, 200
Ejemplo n.º 3
0
    def put(self, type, parent, child):
        """
        ---
        summary: Link existing objects
        description: |
            Add new relation between existing objects.

            Requires `adding_parents` capability.
        security:
            - bearerAuth: []
        tags:
            - relations
        parameters:
            - in: path
              name: type
              schema:
                type: string
                enum: [file, config, blob, object]
              description: Type of parent object
            - in: path
              name: parent
              description: Identifier of the parent object
              required: true
              schema:
                type: string
            - in: path
              name: child
              description: Identifier of the child object
              required: true
              schema:
                type: string
        responses:
            200:
                description: When relation was successfully added
            403:
                description: When user doesn't have `adding_parents` capability.
            404:
                description: |
                    When one of objects doesn't exist or user
                    doesn't have access to object.
        """
        parent_object = access_object(type, parent)
        if parent_object is None:
            raise NotFound("Parent object not found")

        child_object = Object.access(child)
        if child_object is None:
            raise NotFound("Child object not found")

        child_object.add_parent(parent_object, commit=False)

        db.session.commit()
        logger.info(
            "Child added",
            extra={
                "parent": parent_object.dhash,
                "child": child_object.dhash
            },
        )
Ejemplo n.º 4
0
    def delete(self, identifier):
        """
        ---
        summary: Unmark favorite object
        description: |
            Unmark an object as a favorite for the user.

            Requires `personalize` capability.
        security:
            - bearerAuth: []
        tags:
            - object
        parameters:
            - in: path
              name: identifier
              schema:
                type: string
              description: Object identifier
        responses:
            200:
                description: Unmark favorite object
            400:
                description: When request body is invalid
            403:
                description: When user doesn't have `personalize` capability.
            404:
                description: When object doesn't exist.
            503:
                description: |
                    Request canceled due to database statement timeout.
        """

        user = g.auth_user

        favorite_object = Object.access(identifier)

        if favorite_object is None:
            raise NotFound("Object not found")

        if not favorite_object.favorite:
            logger.info(
                "Object is not marked as a favorite",
                extra={
                    "user": user.login,
                    "object_id": identifier
                },
            )
        else:
            favorite_object.followers.remove(user)
            db.session.commit()
            logger.info(
                "Object unmarked as favorite",
                extra={
                    "user": user.login,
                    "object_id": identifier
                },
            )
Ejemplo n.º 5
0
    def create_object(self, params):
        params = dict(params)

        # Validate parent object
        if params["parent"] is not None:
            if not g.auth_user.has_rights(Capabilities.adding_parents):
                raise Forbidden("You are not permitted to link with parent")

            parent_object = Object.access(params["parent"])

            if parent_object is None:
                raise NotFound("Parent object not found")
        else:
            parent_object = None

        # Validate metakeys
        metakeys = params["metakeys"]
        for metakey in params["metakeys"]:
            key = metakey["key"]
            if not MetakeyDefinition.query_for_set(key).first():
                raise NotFound(
                    f"Metakey '{key}' not defined or insufficient "
                    "permissions to set that one"
                )

        # Validate upload_as argument
        share_with = get_shares_for_upload(params["upload_as"])

        item, is_new = self._create_object(params, parent_object, share_with, metakeys)

        try:
            db.session.commit()

            if is_new:
                hooks.on_created_object(item)
                self.on_created(item)
            else:
                hooks.on_reuploaded_object(item)
                self.on_reuploaded(item)
        finally:
            item.release_after_upload()

        logger.info(
            f"{self.ObjectType.__name__} added",
            extra={"dhash": item.dhash, "is_new": is_new},
        )
        schema = self.ItemResponseSchema()
        return schema.dump(item)
Ejemplo n.º 6
0
    def delete(self, identifier):
        """
        ---
        summary: Unmark favorite object
        description: |
            Unmark an object as a favorite for the user.
        security:
            - bearerAuth: []
        tags:
            - object
        parameters:
            - in: path
              name: identifier
              schema:
                type: string
              description: Object identifier
        responses:
            200:
                description: Unmark favorite object
            400:
                description: When request body is invalid
            404:
                description: When object doesn't exist.
        """

        user = g.auth_user

        favorite_object = Object.access(identifier)

        if favorite_object is None:
            raise NotFound("Object not found")

        if not favorite_object.favorite:
            logger.info('Object is not marked as a favorite',
                        extra={
                            'user': user.login,
                            'object_id': identifier
                        })
        else:
            favorite_object.followers.remove(user)
            db.session.commit()
            logger.info('Object unmarked as favorite',
                        extra={
                            'user': user.login,
                            'object_id': identifier
                        })
Ejemplo n.º 7
0
    def create_object(self, params):
        params = dict(params)

        # Validate parent object
        if params["parent"] is not None:
            if not g.auth_user.has_rights(Capabilities.adding_parents):
                raise Forbidden("You are not permitted to link with parent")

            parent_object = Object.access(params["parent"])

            if parent_object is None:
                raise NotFound("Parent object not found")
        else:
            parent_object = None

        # Validate metakeys and Karton assignment
        analysis_id = params.get("karton_id")

        if params["metakeys"]:
            # If 'metakeys' are defined: keep legacy behavior
            if "attributes" in params and params["attributes"]:
                raise BadRequest(
                    "'attributes' and 'metakeys' options can't be mixed")

            attributes = params["metakeys"]
            for attribute in params["metakeys"]:
                key = attribute["key"]
                if key == "karton":
                    if analysis_id is not None:
                        raise BadRequest(
                            "You can't provide more than one Karton analysis identifier"
                        )
                    try:
                        analysis_id = UUID(attribute["value"])
                    except (ValueError, AttributeError):
                        raise BadRequest(
                            "'karton' attribute accepts only UUID values")
                elif not AttributeDefinition.query_for_set(key).first():
                    raise NotFound(
                        f"Attribute '{key}' not defined or insufficient "
                        "permissions to set that one")
        else:
            # If not, rely on 'attributes'
            attributes = params["attributes"]
            for attribute in params["attributes"]:
                key = attribute["key"]
                if not AttributeDefinition.query_for_set(key).first():
                    raise NotFound(
                        f"Attribute '{key}' not defined or insufficient "
                        "permissions to set that one")

        if analysis_id is not None:
            if not g.auth_user.has_rights(Capabilities.karton_assign):
                raise Forbidden(
                    "You are not permitted to assign Karton analysis to object"
                )

        # Validate upload_as argument
        share_with = get_shares_for_upload(params["upload_as"])

        # Tags argument
        tags = params.get("tags")

        item, is_new = self._create_object(params, parent_object, share_with,
                                           attributes, analysis_id, tags)

        try:
            db.session.commit()

            if is_new:
                self.on_created(item, params)
            else:
                self.on_reuploaded(item, params)
        finally:
            item.release_after_upload()

        logger.info(
            f"{self.ObjectType.__name__} added",
            extra={
                "dhash": item.dhash,
                "is_new": is_new
            },
        )
        schema = self.ItemResponseSchema()
        return schema.dump(item)
Ejemplo n.º 8
0
    def get(self):
        """
        ---
        summary: Search or list objects
        description: |
            Returns list of objects matching provided query,
            ordered from the latest one.

            Limited to 10 objects, use `older_than` parameter to fetch more.

            Don't rely on maximum count of returned objects
            because it can be changed/parametrized in future.
        security:
            - bearerAuth: []
        tags:
            - object
        parameters:
            - in: query
              name: older_than
              schema:
                type: string
              description: |
                Fetch objects which are older than the object
                specified by identifier.

                Used for pagination
              required: false
            - in: query
              name: query
              schema:
                type: string
              description: Filter results using Lucene query
              required: false
        responses:
            200:
                description: List of objects
                content:
                  application/json:
                    schema: ObjectListResponseSchema
            400:
                description: |
                    When wrong parameters were provided
                    or syntax error occurred in Lucene query
            404:
                description: When user doesn't have access to the `older_than` object
            503:
                description: |
                    Request canceled due to database statement timeout.
        """
        if "page" in request.args:
            logger.warning("'%s' used legacy 'page' parameter",
                           g.auth_user.login)

        obj = load_schema(request.args, ObjectListRequestSchema())

        pivot_obj = None
        if obj["older_than"]:
            pivot_obj = Object.access(obj["older_than"])
            if pivot_obj is None:
                raise NotFound(
                    "Object specified in 'older_than' parameter not found")

        query = obj["query"]
        if query:
            try:
                db_query = SQLQueryBuilder().build_query(
                    query, queried_type=self.ObjectType)
            except SQLQueryBuilderBaseException as e:
                raise BadRequest(str(e))
            except ParseError as e:
                raise BadRequest(str(e))
        else:
            db_query = db.session.query(self.ObjectType)

        db_query = db_query.filter(g.auth_user.has_access_to_object(
            Object.id)).order_by(Object.id.desc())
        if pivot_obj:
            db_query = db_query.filter(Object.id < pivot_obj.id)
        # Legacy parameter - to be removed in the future
        elif obj["page"] is not None and obj["page"] > 1:
            db_query = db_query.offset((obj["page"] - 1) * 10)

        objects = db_query.limit(10).all()

        schema = self.ListResponseSchema()
        return schema.dump(objects, many=True)
Ejemplo n.º 9
0
    def create_object(self, params):
        params = dict(params)

        # Validate parent object
        if params["parent"] is not None:
            if not g.auth_user.has_rights(Capabilities.adding_parents):
                raise Forbidden("You are not permitted to link with parent")

            parent_object = Object.access(params["parent"])

            if parent_object is None:
                raise NotFound("Parent object not found")
        else:
            parent_object = None

        # Validate metakeys
        metakeys = params["metakeys"]
        for metakey in params["metakeys"]:
            key = metakey["key"]
            if not MetakeyDefinition.query_for_set(key).first():
                raise NotFound(f"Metakey '{key}' not defined or insufficient "
                               "permissions to set that one")

        # Validate upload_as argument
        upload_as = params["upload_as"]
        if upload_as == "*":
            # If '*' is provided: share with all user's groups except 'public'
            share_with = [
                group for group in g.auth_user.groups if group.name != "public"
            ]
        else:
            share_group = Group.get_by_name(upload_as)
            # Does group exist?
            if share_group is None:
                raise NotFound(f"Group {upload_as} doesn't exist")
            # Has user access to group?
            if share_group not in g.auth_user.groups and not g.auth_user.has_rights(
                    Capabilities.sharing_objects):
                raise NotFound(f"Group {upload_as} doesn't exist")
            # Is group pending?
            if share_group.pending_group is True:
                raise NotFound(f"Group {upload_as} is pending")
            share_with = [share_group, Group.get_by_name(g.auth_user.login)]

        item, is_new = self._create_object(params, parent_object, share_with,
                                           metakeys)

        db.session.commit()

        if is_new:
            hooks.on_created_object(item)
            self.on_created(item)
        else:
            hooks.on_reuploaded_object(item)
            self.on_reuploaded(item)

        logger.info(f'{self.ObjectType.__name__} added',
                    extra={
                        'dhash': item.dhash,
                        'is_new': is_new
                    })
        schema = self.ItemResponseSchema()
        return schema.dump(item)
Ejemplo n.º 10
0
    def delete(self, type, parent, child):
        """
        ---
        summary: Remove relation between existing objects
        description: |
            Remove relation between existing objects with permission inheritance.

            Requires `adding_parents` capability.
        security:
            - bearerAuth: []
        tags:
            - relations
        parameters:
            - in: path
              name: type
              schema:
                type: string
                enum: [file, config, blob, object]
              description: Type of parent object
            - in: path
              name: parent
              description: Identifier of the parent object
              required: true
              schema:
                type: string
            - in: path
              name: child
              description: Identifier of the child object
              required: true
              schema:
                type: string
        responses:
            200:
                description: When relation was successfully removed.
            403:
                description: When user doesn't have `removing_parents` capability.
            404:
                description: |
                    When one of objects doesn't exist or user
                    doesn't have access to object.
            503:
                description: |
                    Request canceled due to database statement timeout.
        """
        parent_object = access_object(type, parent)
        if parent_object is None:
            raise NotFound("Parent object not found")

        child_object = Object.access(child)
        if child_object is None:
            raise NotFound("Child object not found")

        result = child_object.remove_parent(parent_object)
        if not result:
            # Relation already removed
            return

        hooks.on_removed_relation(parent_object, child_object)
        if parent_object.id != child_object.id:
            hooks.on_changed_object(parent_object)
            hooks.on_changed_object(child_object)
        else:
            hooks.on_changed_object(parent_object)

        logger.info(
            "Child removed",
            extra={
                "parent": parent_object.dhash,
                "child": child_object.dhash
            },
        )