Esempio n. 1
0
    def resolveQuery(self, session, query):
        """Get the object IDs that match a query.

        @param session: The L{FluidinfoSession} for the request.
        @param query: The query to resolve.
        @raise TBadRequest: If the given query is not encoded properly.
        @raise TParseError: If the query is not well formed.
        @raise TNonexistentTag: If the user doesn't have read permissions
            on the tags in the query.
        @return: A C{Deferred} that will fire with a C{list} of object ID
            C{str}s that match the query.
        """
        try:
            query = query.decode('utf-8')
        except UnicodeDecodeError as error:
            session.log.exception(error)
            error = TBadRequest('Query string %r was not valid UTF-8.' % query)
            return fail(error)
        try:
            parsedQuery = parseQuery(query)
        except QueryParseError as error:
            session.log.exception(error)
            return fail(TParseError(query, error.message))
        except IllegalQueryError as error:
            return fail(TBadRequest(str(error)))

        def run():
            objects = SecureObjectAPI(session.auth.user)
            objectIDs = self._resolveQuery(session, objects, parsedQuery)
            return [str(objectID) for objectID in objectIDs]

        return session.transact.run(run)
Esempio n. 2
0
        def run():
            if not request.isSecure() and not getDevelopmentMode():
                raise TBadRequest(
                    '/users/<username>/verify requests must use HTTPS')
            dictionary = registry.checkRequest(usage, request)
            user = cachingGetUser(self.username.decode('utf-8'))
            if not user:
                raise TNoSuchUser(self.username)
            password = dictionary['password']

            if checkPassword(password, user.passwordHash):
                # FIXME Hard-coding the 'anon' consumer here isn't great,
                # but for now it means we don't have to change the public
                # API. -jkakar
                api = OAuthConsumerAPI()
                consumer = cachingGetUser(u'anon')
                accessToken = api.getAccessToken(consumer, user)
                renewalToken = api.getRenewalToken(consumer, user)
                return {'accessToken': accessToken.encrypt(),
                        'fullname': user.fullname,
                        'renewalToken': renewalToken.encrypt(),
                        'role': str(user.role),
                        'valid': True}
            else:
                return {'valid': False}
Esempio n. 3
0
    def getRecentObjectActivity(self, session, objectID):
        """Get information about recent tag values on the given object.

        @param session: The L{FluidinfoSession} for the request.
        @param objectID: A C{str} representing the ID of the object.
        @return: A C{list} of C{dict}s matching the following format::

             [{'tag': <path>,
               'id': <object-id>,
               'about': <about-value>,
               'value': <value>,
               'username': <username>,
               'updated-at': <timestamp>},
               ...]
        """
        try:
            objectID = UUID(objectID)
        except ValueError:
            error = TBadRequest('Invalid UUID: %r.' % objectID)
            return fail(error)

        def run():
            recentActivity = SecureRecentActivityAPI(session.auth.user)
            result = recentActivity.getForObjects([objectID])
            return self._formatResult(result)

        return session.transact.run(run)
Esempio n. 4
0
    def deleteValuesForQuery(self, session, query, tags=None):
        """Delete L{TagValue}s that match a query.

        @param session: The L{FluidinfoSession} for the request.
        @param query: The query to resolve.
        @param tags: Optionally, the sequence of L{Tag.path}s to delete values
            for.
        @raise TNonexistentTag: Raised if any of the L{Tag}s in the
            L{Query} or to delete does not exist.
        @raise TPathPermissionDenied: Raised if the L{User} does not have
            L{Operation.READ_TAG_VALUE} permission on any of the L{Tag}s in the
            L{Query} or does not have L{Operation.DELETE_TAG_VALUE} permission
            on any of the L{Tag}s to set.
        @raise TParseError: Raised if the L{Query} can't be parsed.
        """
        try:
            parsedQuery = parseQuery(query.decode('utf-8'))
        except QueryParseError as error:
            session.log.exception(error)
            return fail(TParseError(query, error.message))
        except IllegalQueryError as error:
            return fail(TBadRequest(str(error)))
        if tags is not None:
            tags = [tag.decode('utf-8') for tag in tags]

        def run():
            tagValues = SecureTagValueAPI(session.auth.user)
            objects = SecureObjectAPI(session.auth.user)
            objectIDs = self._resolveQuery(session, objects, parsedQuery)
            values = []

            if tags is None:
                # delete all tags user has permissions for
                result = objects.getTagsByObjects(objectIDs,
                                                  Operation.DELETE_TAG_VALUE)
                for objectID, paths in result.iteritems():
                    for path in paths:
                        values.append((objectID, path))
            else:
                # delete only tags requested by user
                result = objects.getTagsByObjects(objectIDs)
                for objectID, paths in result.iteritems():
                    for path in paths:
                        if tags is None or path in tags:
                            values.append((objectID, path))

            if values:
                try:
                    tagValues.delete(values)
                except UnknownPathError as error:
                    session.log.exception(error)
                    path = error.paths[0]
                    raise TNonexistentTag(path.encode('utf-8'))
                except PermissionDeniedError as error:
                    session.log.exception(error)
                    path_, operation = error.pathsAndOperations[0]
                    category, action = getCategoryAndAction(operation)
                    raise TPathPermissionDenied(category, action, path_)

        return session.transact.run(run)
Esempio n. 5
0
    def getPermission(self, session, category, action, path):
        """Get permissions for a given path.

        @param session: The L{AuthenticatedSession} for the request.
        @param category: A C{unicode} indicating the category of the
            permission.
        @param action: A C{unicode} indicating the action of the permission.
        @param path: The L{Namespace.path} or L{Tag.path} to get permissions
            from.
        @raise TBadRequest: Raised if the given C{action} or C{category} are
            invalid.
        @raise TNonexistentNamespace: Raised if the given L{Namespace} path
            does not exist.
        @raise TNonexistentTag: Raised if the given L{Tag} path does not exist.
        @raise TPathPermissionDenied: Raised if the user does not have
            C{CONTROL} permissions on the given L{Namespace} or L{Tag}.
        @return: A C{Deferred} that will fire with a L{TPolicyAndExceptions}
            object containing the policy and exceptions list for the requested
            permission.
        """
        path = path.decode('utf-8')

        try:
            operation = getOperation(category, action)
        except KeyError as error:
            session.log.exception(error)
            error = TBadRequest('Action %r not possible on category %r.' %
                                (action, category))
            return defer.fail(error)

        def run():
            permissions = SecurePermissionAPI(session.auth.user)
            try:
                result = permissions.get([(path, operation)])
            except UnknownPathError as error:
                session.log.exception(error)
                unknownPath = error.paths[0]
                if operation in Operation.TAG_OPERATIONS:
                    raise TNonexistentTag(unknownPath.encode('utf-8'))
                if operation in Operation.NAMESPACE_OPERATIONS:
                    raise TNonexistentNamespace(unknownPath.encode('utf-8'))
                raise
            except PermissionDeniedError as error:
                session.log.exception(error)
                deniedPath, deniedOperation = error.pathsAndOperations[0]
                deniedCategory, deniedAction = getCategoryAndAction(
                    deniedOperation)
                raise TPathPermissionDenied(deniedPath, deniedCategory,
                                            deniedAction)

            policy, exceptions = result[(path, operation)]
            policy = str(policy).lower()
            return TPolicyAndExceptions(policy=policy, exceptions=exceptions)

        return session.transact.run(run)
Esempio n. 6
0
    def getRecentUserActivityForQuery(self, session, query):
        """
        Get information about recent tag values by the users whose objects are
        returned by the given query.

        @param session: The L{FluidinfoSession} for the request.
        @param query: A UTF-8 C{str} with the query to resolve.
        @return: A C{list} of C{dict}s matching the following format::

             [{'tag': <path>,
               'id': <object-id>,
               'about': <about-value>,
               'value': <value>,
               'username': <username>,
               'updated-at': <timestamp>},
               ...]
        """
        try:
            # Extend the query to get only objects for users.
            query = '(%s) AND HAS fluiddb/users/username' % query
            parsedQuery = parseQuery(query.decode('utf-8'))
        except QueryParseError as error:
            session.log.exception(error)
            return fail(TParseError(query, error.message))
        except IllegalQueryError as error:
            return fail(TBadRequest(str(error)))

        def run():
            objects = SecureObjectAPI(session.auth.user)

            # _resolveQuery is implemented in FacadeTagValueMixin
            objectIDs = self._resolveQuery(session, objects, parsedQuery)

            if not objectIDs:
                return []

            # FIXME: This sucks, but right now if the query returns too many
            # objects, RecentActivityAPI will blow up. While we fix this, it's
            # better to return a 400 than a 500.
            if len(objectIDs) > 2000:
                raise TBadRequest('The given query returns to many objects.')

            values = SecureTagValueAPI(session.auth.user)
            result = values.get(objectIDs, [u'fluiddb/users/username'])
            usernames = [result[objectID][u'fluiddb/users/username'].value
                         for objectID in result]

            recentActivity = SecureRecentActivityAPI(session.auth.user)
            result = recentActivity.getForUsers(usernames)
            return self._formatResult(result)

        return session.transact.run(run)
Esempio n. 7
0
        def run():
            objects = SecureObjectAPI(session.auth.user)

            # _resolveQuery is implemented in FacadeTagValueMixin
            objectIDs = self._resolveQuery(session, objects, parsedQuery)

            # FIXME: This sucks, but right now if the query returns too many
            # objects, RecentActivityAPI will blow up. While we fix this, it's
            # better to return a 400 than a 500.
            if len(objectIDs) > 2000:
                raise TBadRequest('The given query returns to many objects.')

            recentActivity = SecureRecentActivityAPI(session.auth.user)
            result = recentActivity.getForObjects(objectIDs)
            return self._formatResult(result)
Esempio n. 8
0
 def run():
     try:
         SecureUserAPI(session.auth.user).delete([username])
     except UnknownUserError as error:
         session.log.exception(error)
         raise TNoSuchUser(username)
     except PermissionDeniedError as error:
         session.log.exception(error)
         deniedPath, operation = error.pathsAndOperations[0]
         deniedPath = deniedPath.encode('utf-8')
         category, action = getCategoryAndAction(operation)
         raise TPathPermissionDenied(category, action, deniedPath)
     except NotEmptyError as error:
         session.log.exception(error)
         raise TBadRequest("Can't delete user %r because they have "
                           'data.' % username)
Esempio n. 9
0
    def updateUser(self, session, info):
        """Update information about a L{User}.

        @param session: The L{AuthenticatedSession} for the request.
        @param info: A L{TUserUpdate} with information about a L{User}.
        @raise TNoSuchUser: Raised if the specified L{User} doesn't exist.
        @raise TPathPermissionDenied: Raised if the L{User} requesting the
            update is not a superuser.
        @return: A C{Deferred} that will fire after the update has been
            performed.
        """
        info.username = info.username.decode('utf-8').lower()
        if info.password is not None:
            info.password = info.password.decode('utf-8')
        if info.name is not None:
            info.name = info.name.decode('utf-8')
        if info.email is not None:
            info.email = info.email.decode('utf-8')
        if info.role is not None:
            try:
                info.role = Role.fromName(info.role)
            except LookupError:
                return fail(TBadRequest('Invalid role given.'))

        def run():
            try:
                [(objectID, _)] = SecureUserAPI(session.auth.user).set([
                    (info.username, info.password, info.name, info.email,
                     info.role)
                ])
            except UnknownUserError as error:
                session.log.exception(error)
                raise TNoSuchUser(info.username.encode('utf-8'))
            except PermissionDeniedError as error:
                session.log.exception(error)
                deniedPath, operation = error.pathsAndOperations[0]
                deniedPath = deniedPath.encode('utf-8')
                category, action = getCategoryAndAction(operation)
                raise TPathPermissionDenied(category, action, deniedPath)

            return str(objectID)

        return session.transact.run(run)
Esempio n. 10
0
        def run():
            objects = SecureObjectAPI(session.auth.user)

            # _resolveQuery is implemented in FacadeTagValueMixin
            objectIDs = self._resolveQuery(session, objects, parsedQuery)

            if not objectIDs:
                return []

            # FIXME: This sucks, but right now if the query returns too many
            # objects, RecentActivityAPI will blow up. While we fix this, it's
            # better to return a 400 than a 500.
            if len(objectIDs) > 2000:
                raise TBadRequest('The given query returns to many objects.')

            values = SecureTagValueAPI(session.auth.user)
            result = values.get(objectIDs, [u'fluiddb/users/username'])
            usernames = [result[objectID][u'fluiddb/users/username'].value
                         for objectID in result]

            recentActivity = SecureRecentActivityAPI(session.auth.user)
            result = recentActivity.getForUsers(usernames)
            return self._formatResult(result)
Esempio n. 11
0
    def updatePermission(self, session, category, action, path,
                         policyAndExceptions):
        """Update permissions for a given path.

        @param session: The L{AuthenticatedSession} for the request.
        @param category: A C{unicode} indicating the category of the
            permission.
        @param action: A C{unicode} indicating the action of the permission.
        @param path: The L{Namespace.path} or L{Tag.path} to get permissions
            from.
        @param policyAndExceptions: A L{TPolicyAndExceptions} object containing
            the policy and exceptions list for the permission.
        @raise TBadRequest: Raised if the given C{action} or C{category} are
            invalid.
        @raise TInvalidPolicy: Raised if the policy given in
            C{policyAndExceptions} is invalid.
        @raise TNonexistentNamespace: Raised if the given L{Namespace} path
            does not exist.
        @raise TNonexistentTag: Raised if the given L{Tag} path does not exist.
        @raise TPathPermissionDenied: Raised if the user does not have
            C{CONTROL} permissions on the given L{Namespace} or L{Tag}.
        @return: A C{Deferred} that will fire with a C{None} if the operation
            was successful.
        """
        path = path.decode('utf-8')

        try:
            operation = getOperation(category, action)
        except KeyError as error:
            session.log.exception(error)
            error = TBadRequest('Action %r not possible on category %r.' %
                                (action, category))
            return defer.fail(error)

        policy = policyAndExceptions.policy
        if policy not in ('open', 'closed'):
            return defer.fail(TInvalidPolicy())
        policy = Policy.OPEN if policy == 'open' else Policy.CLOSED
        exceptions = policyAndExceptions.exceptions

        def run():
            permissions = SecurePermissionAPI(session.auth.user)
            try:
                permissions.set([(path, operation, policy, exceptions)])
            except UnknownPathError as error:
                session.log.exception(error)
                unknownPath = error.paths[0]
                if operation in Operation.TAG_OPERATIONS:
                    raise TNonexistentTag(unknownPath.encode('utf-8'))
                if operation in Operation.NAMESPACE_OPERATIONS:
                    raise TNonexistentNamespace(unknownPath.encode('utf-8'))
                raise
            except UnknownUserError as error:
                # FIXME There could be more than one unknown username, but
                # TNoSuchUser can only be passed a single username, so we'll
                # only pass the first one.  Ideally, we'd be able to pass all
                # of them.
                raise TNoSuchUser(error.usernames[0].encode('utf-8'))
            except UserNotAllowedInExceptionError as error:
                raise TInvalidUsername(str(error))
            except PermissionDeniedError as error:
                session.log.exception(error)
                deniedPath, deniedOperation = error.pathsAndOperations[0]
                deniedCategory, deniedAction = getCategoryAndAction(
                    deniedOperation)
                raise TPathPermissionDenied(deniedPath, deniedCategory,
                                            deniedAction)

        return session.transact.run(run)
Esempio n. 12
0
    def updateValuesForQueries(self, session, valuesQuerySchema):
        """Set L{TagValue}s that match a list of L{Query}s.

        @param session: The L{FluidinfoSession} for the request.
        @param valuesQuerySchema: L{ValuesQuerySchema}
        @raise TNonexistentTag: Raised if any of the L{Tag}s in the
            L{Query} or to set does not exist.
        @raise TNonexistentTag: Raised if the L{User} does not have
            L{Opeartion.READ_TAG_VALUE} permission on any of the L{Tag}s in the
            L{Query}.
        @raise TPathPermissionDenied: Raised if the L{User} does not have
            L{Operation.WRITE_TAG_VALUE} permission on any of the L{Tag}s to
            set.
        @raise TParseError: Raised if the L{Query} can't be parsed.
        """
        # Parse queries in the reactor thread, the query parser is not thread
        # safe.
        valuesByQuery = {}
        for query, tagsAndValues in valuesQuerySchema.queryItems:
            try:
                parsedQuery = parseQuery(query)
            except QueryParseError as error:
                session.log.exception(error)
                return fail(TParseError(query, error.message))
            except IllegalQueryError as error:
                return fail(TBadRequest(str(error)))
            valuesByQuery[parsedQuery] = tagsAndValues

        def run():
            objects = SecureObjectAPI(session.auth.user)

            try:
                searchQueries = objects.search(valuesByQuery.keys())
            except UnknownPathError as error:
                session.log.exception(error)
                unknownPath = error.paths[0]
                raise TNonexistentTag(unknownPath.encode('utf-8'))
            except PermissionDeniedError as error:
                session.log.exception(error)
                path_, operation = error.pathsAndOperations[0]
                if operation == Operation.CREATE_OBJECT:
                    raise TUnauthorized()
                else:
                    raise TNonexistentTag(path_)

            # Run queries.
            try:
                with session.timer.track('index-search'):
                    result = blockingCallFromThread(reactor, searchQueries.get)
            except SearchError as error:
                session.log.exception(error)
                raise TParseError(query, error.message)

            # Build a result set from the searches.
            values = {}
            for parsedQuery, objectIDs in result.iteritems():
                for objectID in objectIDs:
                    for tagAndValue in valuesByQuery[parsedQuery]:
                        value = guessValue(tagAndValue.value)
                        # FIXME: this code sucks, but I rather not having
                        # to modify guessValue to return a list, as that
                        # would break other code.
                        # Hopefully, we'll be able to remove this pretty
                        # soon.
                        if isinstance(value, list):
                            value = [item.decode('utf-8') for item in value]
                        if objectID not in values:
                            values[objectID] = {}
                        values[objectID][tagAndValue.path] = value

            # Update values.
            if values:
                tagValues = SecureTagValueAPI(session.auth.user)
                try:
                    result = tagValues.set(values)
                except UnknownPathError as error:
                    session.log.exception(error)
                    path = error.paths[0]
                    raise TNonexistentTag(path.encode('utf-8'))
                except MalformedPathError as error:
                    # FIXME: Modify MalformedPathError to have a path field.
                    raise TInvalidPath(str(error).encode('utf-8'))
                except PermissionDeniedError as error:
                    session.log.exception(error)
                    path_, operation = error.pathsAndOperations[0]
                    category, action = getCategoryAndAction(operation)
                    raise TPathPermissionDenied(category, action, path_)

        return session.transact.run(run)
Esempio n. 13
0
    def getValuesForQuery(self, session, query, tags=None):
        """Get L{TagValue}s that match a query.

        Existence checks are performed for L{Tag.path}s specified in the
        L{Query}, but not in the return list.  If requested tags don't exist
        we treat them as having no matches.

        @param session: The L{FluidinfoSession} for the request.
        @param query: The query to resolve.
        @param tags: Optionally, the sequence of L{Tag.path}s to retrieve
            values for.
        @raise TNonexistentTag: Raised if L{Tag}s in the L{Query} don't exist,
            or if the L{User} doesn't have L{Operation.READ_TAG_VALUE}
            permission on all L{Tag}s in the query.
        @raise TParseError: Raised if the L{Query} can't be parsed.
        @return: A L{Deferred} that will fire with a C{dict} that maps object
            IDs to L{Tag.path}s with L{TagValue}s, matching the following
            format::

              {'results': {
                  'id': {<object-id>: {<path>: {'value': <contents>},
                                       <path>: {'value-type': <contents>,
                                                'size': <size>}}}}}

            We only return the value type and the size for binary L{TagValue}s
            since JSON doesn't support binary strings.
        """
        try:
            parsedQuery = parseQuery(query.decode('utf-8'))
        except QueryParseError as error:
            session.log.exception(error)
            return fail(TParseError(query, error.message))
        except IllegalQueryError as error:
            return fail(TBadRequest(str(error)))
        if tags is not None:
            tags = [tag.decode('utf-8') for tag in tags]

        def run():
            tagValues = SecureTagValueAPI(session.auth.user)
            objects = SecureObjectAPI(session.auth.user)
            objectIDs = self._resolveQuery(session, objects, parsedQuery)
            if not objectIDs:
                return dumps({'results': {'id': {}}})
            try:
                values = tagValues.get(objectIDs, tags)
            except UnknownPathError as error:
                # One or more of the requested return Tag's doesn't exist.
                # We'll filter them out and try again because we don't want to
                # fail the request just because of a missing tag.
                filteredTags = set(tags) - set(error.paths)
                if not filteredTags:
                    return dumps({'results': {'id': {}}})
                values = tagValues.get(objectIDs, filteredTags)
            except PermissionDeniedError as error:
                session.log.exception(error)
                path_, operation = error.pathsAndOperations[0]
                raise TNonexistentTag(path_)

            valuesByObjectID = {}
            for objectID, tagPaths in values.iteritems():
                for tagPath, tagValue in tagPaths.iteritems():
                    value = tagValue.value
                    if isinstance(value, dict):
                        size = len(value[u'contents'])
                        mimeType = value[u'mime-type']
                        value = {u'value-type': mimeType,
                                 u'size': size}
                    elif isinstance(value, UUID):
                        value = {'value': str(value)}
                    else:
                        value = {'value': value}
                    value['updated-at'] = tagValue.creationTime.isoformat()
                    value['username'] = tagValue.creator.username
                    objectID = str(objectID)
                    valuesByObjectID.setdefault(objectID, {})[tagPath] = value
            result = {'results': {'id': valuesByObjectID}}
            return dumps(result)

        return session.transact.run(run)