def getUnknownParentPaths(self, unknownPaths): """Get a C{dict} mapping unknown paths to their closest L{Namespace}. This function finds the closest L{Namespace} parent for the specified unknown paths. It walks back down each path until it finds a parent or determines that one doesn't exist. @param unknownPaths: A C{set} of unknown L{Tag.path}s and L{Namespace.path}s. @return: A C{dict} that maps unknown paths to their closest L{Namepace.path} parent. """ if not unknownPaths: return {} hierarchy = getPathHierarchy(unknownPaths) existingPaths = getNamespaces(paths=hierarchy).values(Namespace.path) existingPaths = set(existingPaths) closestParents = {} for path in unknownPaths: parentPath = getParentPath(path) while parentPath is not None: if parentPath in existingPaths: closestParents[path] = parentPath break parentPath = getParentPath(parentPath) return closestParents
def getUnknownParentPaths(self, unknownPaths): """Get a C{dict} mapping unknown paths to their closest L{Namespace}. This function finds the closest L{Namespace} parent for the specified unknown paths. It walks back down each path until it finds a parent or determines that one doesn't exist. @param unknownPaths: A C{set} of unknown L{Tag.path}s and L{Namespace.path}s. @return: A C{dict} that maps unknown paths to their closest L{Namepace.path} parent. """ if not unknownPaths: return {} hierarchy = getPathHierarchy(unknownPaths) existingPaths = getNamespaces(paths=hierarchy).values(Namespace.path) existingPaths = set(existingPaths) closestParents = {} for path in unknownPaths: parentPath = getParentPath(path) while parentPath is not None: if parentPath in existingPaths: closestParents[path] = parentPath break parentPath = getParentPath(parentPath) return closestParents
def check(self, namespaces): """ Check given L{Namespace}s for integrity errors. @param namespaces: A sequence of L{Namespace}s to be checked. """ aboutValues = self._getValues(namespaces, u'fluiddb/about') pathValues = self._getValues(namespaces, u'fluiddb/namespaces/path') descriptionValues = self._getValues(namespaces, u'fluiddb/namespaces/description') paths = [namespace.path for namespace in namespaces] namespacePermissions = dict(getNamespacePermissions(paths)) parentPaths = [ getParentPath(namespace.path) for namespace in namespaces if namespace.parentID is not None ] parentNamespaces = getNamespaces(paths=parentPaths) parentNamespaces = dict( (namespace.path, namespace) for namespace in parentNamespaces) users = getUsers() users = dict((user.id, user) for user in users) for namespace in namespaces: expectedAbout = u'Object for the namespace %s' % namespace.path self.checkAboutValue(namespace, aboutValues, expectedAbout) self.checkPathValue(namespace, pathValues) self.checkDescriptionValue(namespace, descriptionValues) self.checkPermissions(namespace, namespacePermissions, users, Operation.NAMESPACE_OPERATIONS) self.checkParent(namespace, parentNamespaces) self.checkValidPath(namespace)
def _createTags(self, values, parentNamespaces): """Create new tags. @param values: A sequence of C{(Tag.path, description)} 2-tuples. @param parentNamespaces: A C{dict} mapping L{Namespace.path}s to L{Namespace} instances, the parents of the new L{Tag}s. @return: A C{list} of C{(objectID, Tag.path)} 2-tuples. """ admin = getUser(u'fluiddb') objects = self._factory.objects(admin) systemValues = {} result = [] tags = [] for path, description in values: parentPath = getParentPath(path) name = getPathName(path) parentNamespace = parentNamespaces.get(parentPath) tag = createTag(self._user, parentNamespace, name) tag.objectID = objects.create( u'Object for the attribute %s' % path) result.append((tag.objectID, path)) systemValues[tag.objectID] = { u'fluiddb/tags/description': description, u'fluiddb/tags/path': path } tags.append(tag) self._createPermissions(tags) if systemValues: self._factory.tagValues(admin).set(systemValues) return result
def check(self, tags): """Check given L{Tag}s for integrity errors. @param tags: A sequence of L{Tag}s to be checked. """ aboutValues = self._getValues(tags, u'fluiddb/about') pathValues = self._getValues(tags, u'fluiddb/tags/path') descriptionValues = self._getValues(tags, u'fluiddb/tags/description') tagPermissions = dict(getTagPermissions([tag.path for tag in tags])) parentPaths = [getParentPath(tag.path) for tag in tags] parentNamespaces = getNamespaces(parentPaths) parentNamespaces = dict( (namespace.path, namespace) for namespace in parentNamespaces) users = getUsers() users = dict((user.id, user) for user in users) for tag in tags: expectedAbout = u'Object for the attribute %s' % tag.path self.checkAboutValue(tag, aboutValues, expectedAbout) self.checkPathValue(tag, pathValues) self.checkDescriptionValue(tag, descriptionValues) self.checkPermissions(tag, tagPermissions, users, Operation.TAG_OPERATIONS) self.checkParent(tag, parentNamespaces) self.checkValidPath(tag)
def create(self, values): """Create new L{Namespace}s. Missing parent L{Namespace}s are created automatically. For example, if C{foo/bar/baz} is requested, and C{foo/bar} doesn't already exist, it will be created before C{foo/bar/baz} is created. Associated L{NamespacePermission}s are created automatically with the system-wide default permissions. @param values: A sequence of C{(path, description)} 2-tuples. @raises DuplicatePathError: Raised if the path for a new L{Namespace} collides with an existing one. @raise MalformedPathError: Raised if one of the given paths is empty or has unacceptable characters. @return: A C{list} of C{(objectID, path)} 2-tuples for the new L{Namespace}s. """ from fluiddb.model.user import getUser if not values: return [] paths = [path for path, description in values] descriptions = dict(values) self._checkForDuplicates(paths) admin = getUser(u'fluiddb') objects = self._factory.objects(admin) systemValues = {} paths = getPathHierarchy(paths) existingNamespaces = dict((namespace.path, namespace) for namespace in getNamespaces(paths=paths)) newNamespaces = [] for path in sorted(paths): if path in existingNamespaces: continue parentPath = getParentPath(path) parentID = (existingNamespaces[parentPath].id if parentPath is not None else None) namespace = createNamespace(self._user, path, parentID) aboutValue = u'Object for the namespace %s' % path description = descriptions.get(path, aboutValue) namespace.objectID = objects.create(aboutValue) systemValues[namespace.objectID] = { u'fluiddb/namespaces/description': description, u'fluiddb/namespaces/path': path, u'fluiddb/about': aboutValue} existingNamespaces[path] = namespace newNamespaces.append(namespace) self._createPermissions(newNamespaces) self._factory.tagValues(admin).set(systemValues) values = [(namespace.objectID, namespace.path) for namespace in newNamespaces] return values
def check(self, tags): """Check given L{Tag}s for integrity errors. @param tags: A sequence of L{Tag}s to be checked. """ aboutValues = self._getValues(tags, u'fluiddb/about') pathValues = self._getValues(tags, u'fluiddb/tags/path') descriptionValues = self._getValues(tags, u'fluiddb/tags/description') tagPermissions = dict(getTagPermissions([tag.path for tag in tags])) parentPaths = [getParentPath(tag.path) for tag in tags] parentNamespaces = getNamespaces(parentPaths) parentNamespaces = dict((namespace.path, namespace) for namespace in parentNamespaces) users = getUsers() users = dict((user.id, user) for user in users) for tag in tags: expectedAbout = u'Object for the attribute %s' % tag.path self.checkAboutValue(tag, aboutValues, expectedAbout) self.checkPathValue(tag, pathValues) self.checkDescriptionValue(tag, descriptionValues) self.checkPermissions(tag, tagPermissions, users, Operation.TAG_OPERATIONS) self.checkParent(tag, parentNamespaces) self.checkValidPath(tag)
def check(self, namespaces): """ Check given L{Namespace}s for integrity errors. @param namespaces: A sequence of L{Namespace}s to be checked. """ aboutValues = self._getValues(namespaces, u'fluiddb/about') pathValues = self._getValues(namespaces, u'fluiddb/namespaces/path') descriptionValues = self._getValues(namespaces, u'fluiddb/namespaces/description') paths = [namespace.path for namespace in namespaces] namespacePermissions = dict(getNamespacePermissions(paths)) parentPaths = [getParentPath(namespace.path) for namespace in namespaces if namespace.parentID is not None] parentNamespaces = getNamespaces(paths=parentPaths) parentNamespaces = dict((namespace.path, namespace) for namespace in parentNamespaces) users = getUsers() users = dict((user.id, user) for user in users) for namespace in namespaces: expectedAbout = u'Object for the namespace %s' % namespace.path self.checkAboutValue(namespace, aboutValues, expectedAbout) self.checkPathValue(namespace, pathValues) self.checkDescriptionValue(namespace, descriptionValues) self.checkPermissions(namespace, namespacePermissions, users, Operation.NAMESPACE_OPERATIONS) self.checkParent(namespace, parentNamespaces) self.checkValidPath(namespace)
def summarizeObject(self, about): """Get summary information for an object. @param about: The about value of the object to summarize. @return: A C{dict} matching the following format:: {'commentCount': <count>, 'followers': [<username>, ...], 'relatedObjects': {'<about>': <count>, ...}} """ # List followers. result = self._objects.get([about]) objectID = result.get(about) if objectID is None: return {'commentCount': 0, 'followers': [], 'relatedObjects': {}} paths = self._objects.getTagsForObjects([objectID]) followers = [] for path in paths: parent = getParentPath(path) if parent and not u'/' in parent and path.endswith('/follows'): followers.append(parent) # Count comments. store = getMainStore() result = store.find(CommentObjectLink.commentID, CommentObjectLink.objectID == objectID) commentCount = result.count() # Count related objects. I'm using raw SQL here because don't know how # to translate this query to Storm. result = store.execute(""" SELECT about_tag_values."value", summary.count FROM ( SELECT comment_object_link.object_id AS object_id, COUNT(comment_object_link.comment_id) AS count FROM comment_object_link WHERE comment_object_link.comment_id IN ( SELECT comment_object_link.comment_id FROM comment_object_link WHERE comment_object_link.object_id = '{objectID}') AND comment_object_link.object_id != '{objectID}' GROUP BY comment_object_link.object_id ) AS summary JOIN about_tag_values ON summary.object_id = about_tag_values.object_id; """.format(objectID=objectID)) relatedObjects = dict(result) return { 'commentCount': commentCount, 'followers': followers, 'relatedObjects': relatedObjects }
def _createTags(self): """Create tags.""" superuser = self.users[u'fluiddb'] for tag in self._data['tags']: path = tag['path'] parentPath = getParentPath(path) name = getPathName(path) parentNamespace = self.namespaces[parentPath] tagObject = createTag(superuser, parentNamespace, name) self._createTagPermissions(tagObject) self.tags[path] = tagObject
def create(self, values): """See L{TagAPI.create}. @raises PermissionDeniedError: Raised if the user is not authorized to create L{Tag}s. """ pathsAndOperations = [(getParentPath(path), Operation.CREATE_NAMESPACE) for path, description in values] deniedOperations = checkPermissions(self._user, pathsAndOperations) if deniedOperations: raise PermissionDeniedError(self._user.username, deniedOperations) return self._api.create(values)
def checkParent(self, tag, parentNamespaces): """Check that a given L{Tag} has a correct parent. @param object: A L{Tag} to check. @param parentNamespaces: A C{dict} mapping paths to namespaces representing parents. """ parentPath = getParentPath(tag.path) parent = parentNamespaces.get(parentPath) if tag.namespaceID is None: self._log(tag, 'Parent ID is not specified.') elif not parent or parent.id != tag.namespaceID: self._log(tag, 'Assigned parent is incorrect.')
def _createNamespaces(self): """Create L{Namespace}s.""" superuser = self.users[u'fluiddb'] for namespaceData in self._data['namespaces']: path = namespaceData['path'] parentPath = getParentPath(path) parentNamespace = self.namespaces.get(parentPath, None) parentID = parentNamespace.id if parentNamespace else None namespace = createNamespace(superuser, path, parentID) self._createNamespacePermissions(namespace) self.namespaces[path] = namespace if path in self.users: self.users[path].namespaceID = namespace.id
def _createPermissions(self, tags): """Create L{TagPermission}s for new L{Tag}s. L{Tag}s inherit permissions from their parent tag, if one is available. @param tags: A sequence of new L{Tag}s to create L{TagPermission}s for. """ # Preload parent Namespace and NamespacePermission's into Storm's # cache. Simply creating the objects will be enough to get them into # the cache. If there are many objects we need to be careful about # overloading the cache, but that isn't an issue here. parentPaths = [getParentPath(tag.path) for tag in tags] list(getNamespacePermissions(parentPaths)) for tag in tags: createTagPermission(tag)
def check(self, values): """Check permissions for a L{User} with the L{Role.USER} role. @param values: A sequence of C{(path, Operation)} 2-tuples representing actions that should be checked. @raise UnknownUserError: Raised if a user don't exist for user operations. @return: A C{list} of C{(path, Operation)} 2-tuples representing actions that are denied. """ deniedOperations = [] storedOperations = set() for path, operation in values: # Create object is always allowed for normal users. if operation == Operation.CREATE_OBJECT: continue # Create root namespaces is always denied for normal users. elif path is None and operation == Operation.CREATE_NAMESPACE: deniedOperations.append((path, operation)) continue # Delete root namespaces is always denied for normal users. elif (path is not None and getParentPath(path) is None and operation == Operation.DELETE_NAMESPACE): deniedOperations.append((path, operation)) continue # User managers are always allowed to perform user operations. elif (self._user.role == Role.USER_MANAGER and operation in Operation.USER_OPERATIONS): continue # Updating user data is only allowed for the own user. elif (operation == Operation.UPDATE_USER and self._user.username == path): continue # All other user operations are always denied for normal users. elif operation in Operation.USER_OPERATIONS: deniedOperations.append((path, operation)) continue else: # Operations that have to be checked in the database. storedOperations.add((path, operation)) if not storedOperations: return deniedOperations return deniedOperations + self._getDeniedOperations(storedOperations)
def create(self, values): """See L{NamespaceAPI.create}. @raise PermissionDeniedError: Raised if the user is not authorized to create L{Namespace}s. """ paths = [] pathsAndOperations = [] for path, description in values: parentPath = getParentPath(path) pathsAndOperations.append((parentPath, Operation.CREATE_NAMESPACE)) paths.append(path) deniedOperations = checkPermissions(self._user, pathsAndOperations) if deniedOperations: raise PermissionDeniedError(self._user.username, deniedOperations) return self._api.create(values)
def _createPermissions(self, namespaces): """Create L{NamespacePermission}s for new L{Namespace}s. L{Namespace}s inherit permissions from their parent namespace, if one is available. @param namespaces: A sequence of new L{Namespace}s to create L{NamespacePermission}s for. """ paths = [namespace.path for namespace in namespaces] parentPaths = getParentPaths(paths) index = {} for parent, permission in getNamespacePermissions(parentPaths): index[parent.path] = permission for namespace in sorted(namespaces, key=lambda namespace: namespace.path): parentPath = getParentPath(namespace.path) parentPermission = index.get(parentPath) permission = createNamespacePermission( namespace, permissionTemplate=parentPermission) index[namespace.path] = permission
def testGetParentPath(self): """ L{getParentPath} returns the parent path for non-root-level paths. """ self.assertEqual(u'foo', getParentPath(u'foo/bar')) self.assertEqual(u'foo/bar', getParentPath(u'foo/bar/baz'))
def testGetParentPathWithRootPath(self): """ L{getParentPath} returns C{None} if the path is a root-level path. """ self.assertIdentical(None, getParentPath(u'foo'))