def __init__(self, context, parent=None, groupid=u'', title=u'', description=u'', namespace=u'', roles=(), members=None, **kwargs): if not IWorkspaceContext.providedBy(context): raise ValueError('Could not adapt: context not a workspace') schema = interfaces.IWorkspaceGroup if schema.providedBy(parent): self.__parent__ = parent self.context = context self.adapts_project = IProjectContext.providedBy(context) valid_setattr(self, schema['baseid'], _decode(groupid)) valid_setattr(self, schema['title'], _decode(title)) valid_setattr(self, schema['description'], _decode(description)) valid_setattr(self, schema['namespace'], _decode(namespace)) self.portal = getSite() self.site_members = members or interfaces.ISiteMembers(self.portal) groups = Groups(self.portal) groupname = self.pas_group()[0] if groupname not in groups: groups.add(groupname) # edge-case: may cause write-on-read self._group = GroupInfo(groupname, members=self.site_members)
def test_get_user(self): """Get user from roster""" username = self.some_user workspace, roster = self._base_fixtures() assert username in roster # was added by fixture group = GroupInfo(roster.groups['viewers'].pas_group()[0]) assert username in group # equal propertied user objects: self.assertEqual(group.get(username)._id, roster.get(username)._id) self.assertEqual( group.get(username)._login, roster.get(username)._login)
def test_get_user(self): """Get user from roster""" username = self.some_user workspace, roster = self._base_fixtures() assert username in roster # was added by fixture group = GroupInfo(roster.groups['viewers'].pas_group()[0]) assert username in group # equal propertied user objects: self.assertEqual(group.get(username)._id, roster.get(username)._id) self.assertEqual( group.get(username)._login, roster.get(username)._login )
def test_user_add_and_containment(self): """ Test user addition, containment matches containment in associated group """ workspace, roster = self._base_fixtures() # add a user to the roster, then to the 'managers' group; # test containment/success of both in roster, workgroup, and PAS # group. username = '******' self.site_members.register(username, send=False) original_membercount = len(roster) roster.add(username) roster.groups['managers'].add(username) assert username in roster assert username in roster.groups['managers'] # get PAS group, via IGroup: pas_group = GroupInfo(roster.groups['managers'].pas_group()[0]) assert username in pas_group self.assertEqual(len(roster), original_membercount + 1)
class WorkspaceGroup(object): """ Workspace group adapter: provides mapping for managing workspace membership in a specific group. """ implements(interfaces.IWorkspaceGroup) adapts(IWorkspaceContext) # class attribute defaults (for instance attributes): __parent__ = None def __init__(self, context, parent=None, groupid=u'', title=u'', description=u'', namespace=u'', roles=(), members=None, **kwargs): if not IWorkspaceContext.providedBy(context): raise ValueError('Could not adapt: context not a workspace') schema = interfaces.IWorkspaceGroup if schema.providedBy(parent): self.__parent__ = parent self.context = context self.adapts_project = IProjectContext.providedBy(context) valid_setattr(self, schema['baseid'], _decode(groupid)) valid_setattr(self, schema['title'], _decode(title)) valid_setattr(self, schema['description'], _decode(description)) valid_setattr(self, schema['namespace'], _decode(namespace)) self.portal = getSite() self.site_members = members or interfaces.ISiteMembers(self.portal) groups = Groups(self.portal) groupname = self.pas_group()[0] if groupname not in groups: groups.add(groupname) # edge-case: may cause write-on-read self._group = GroupInfo(groupname, members=self.site_members) @property def __name__(self): return self.baseid or None def _groupname(self): ns = self.namespace # usually UUID of workspace return '-'.join((ns, self.baseid)) def _grouptitle(self): r = [] # stack of object context (LIFO) context = self.context while getattr(context, '__parent__', None) is not None: if ISiteRoot.providedBy(context): break r.append(context) context = context.__parent__ titles = [o.Title() for o in reversed(r)] return u'%s - %s' % (' / '.join(titles).encode('utf-8'), self.title) def pas_group(self): return (self._groupname(), self._grouptitle()) def applyTransform(self, username): return self.site_members.applyTransform(username) def _get_user(self, username): username = self.applyTransform(username) return self.site_members.get(username) def __contains__(self, username): username = self.applyTransform(username) return username in self.keys() def __getitem__(self, username): username = self.applyTransform(username) if username not in self.keys(): raise KeyError('User %s not in group %s (%s)' % ( username, self._groupname(), self._grouptitle(), )) return self._get_user(username) def get(self, username, default=None): username = self.applyTransform(username) if username not in self.keys(): return default return self._get_user(username) # mapping enumeration -- keys/values/items: def keys(self): """ List of login names as group members. This may be cached, as it is expensive to list assigned group principals in the stock Plone group plugin (ZODBGroupManager). """ return self._group.keys() def values(self): return [self._get_user(k) for k in self.keys()] def items(self): return [(k, self._get_user(k)) for k in self.keys()] def __len__(self): return len(self.keys()) # iterable mapping methods: def iterkeys(self): return iter(self.keys()) __iter__ = iterkeys def itervalues(self): return itertools.imap(self._get_user, self.keys()) def iteritems(self): func = lambda username: (username, self._get_user(username)) # (k,v) return itertools.imap(func, self.keys()) # add / delete (assign/unassign) methods: def add(self, username): msg = '' username = self.applyTransform(username) if username not in self.site_members: raise RuntimeError('User %s unknown to site' % username) if username not in self.keys(): self._group.assign(username) user = self.site_members.get(username) fullname = user.getProperty('fullname', '') basemsg = u'Added user %s (%s) to' % ( username, fullname, ) if not self.__parent__: msg = '%s workspace "%s".' % ( basemsg, self.context.Title(), ) else: msg = '%s %s role group in "%s".' % ( basemsg, self.title, self.context.Title() ) if self.__parent__: if username not in self.__parent__: msg = ( 'User %s not allowed in "%s" ' 'without workgroup membership' % ( username, self.baseid )) log_status(msg, self.context, level=logging.ERROR) raise RuntimeError(msg) else: # viewers/base group: parent_workspace = workspace_for(self.context.__parent__) if parent_workspace: parent_roster = WorkspaceRoster(parent_workspace) parent_roster.add(username) if msg: log_status(msg, self.context) self.refresh(username) # invalidate keys -- membership modified. def unassign(self, username): if self.baseid == BASE_GROUPNAME and self.__parent__: # for viewers group, critical that we unassign all groups for user other_groups = filter( lambda g: g is not self, self.__parent__.groups.values() ) for group in other_groups: if username in group.keys(): group.unassign(username) msg = '%s removed from workgroup roster in workspace "%s"' % ( username, self.context.Title(), ) else: msg = 'Removed user %s from group %s in workspace "%s"' % ( username, self.baseid, self.context.Title(), ) log_status(msg, self.context) username = self.applyTransform(username) if username not in self.keys(): raise ValueError('user %s is not group member' % username) self._group.unassign(username) self.refresh() # need to invalidate keys -- membership modified. def refresh(self, username=None): self._group.refresh() if username is not None: username = self.applyTransform(username) userid = self.site_members.userid_for(username) clear_cached_localroles(userid)