def testRolePermission(self): permission = definePermission('APerm', 'aPerm title').id role = defineRole('ARole', 'A Role').id manager.grantPermissionToRole(permission, role) self.assertEqual(manager.getRolesForPermission(permission), [(role,Allow)]) self.assertEqual(manager.getPermissionsForRole(role), [(permission,Allow)])
def zcml_transition_permission(pid, title, roles): ZCML_LINES.append(ZCML_INDENT) ZCML_LINES.append('%s<permission id="%s" title="%s" />' % ( ZCML_INDENT, pid, title)) provideUtility(Permission(pid), IPermission, pid) for role in roles: ZCML_LINES.append( '%s<grant permission="%s" role="%s" />' % (ZCML_INDENT, pid, role)) rpm.grantPermissionToRole(pid, role, check=False)
def reload_roles(stream): log.info("(Re)Loading OMS permission definitions") for line in stream: nick, role, permissions = line.split(':', 4) oms_role = Role(role, nick) provideUtility(oms_role, IRole, role) for perm in permissions.split(','): if perm.strip(): rolePermissionManager.grantPermissionToRole(perm.strip(), role.strip())
def reload_roles(stream): log.info("(Re)Loading OMS permission definitions") for line in stream: nick, role, permissions = line.split(':', 4) oms_role = Role(role, nick) provideUtility(oms_role, IRole, role) for perm in permissions.split(','): if perm.strip(): rolePermissionManager.grantPermissionToRole( perm.strip(), role.strip())
def add_retract_transitions(wf): def getRoles(transition): original_roles = transition.user_data.get("_roles", []) allowed_roles = set() #transitions to source tx_to_source = wf.get_transitions_to(transition.source) for tx in tx_to_source: if tx.source is None: allowed_roles.update(set(original_roles)) else: for role in tx.user_data.get("_roles", []): if role in original_roles: allowed_roles.add(role) if not len(allowed_roles): allowed_roles = original_roles return allowed_roles transitions = wf._transitions_by_id.values() terminal_transitions = [ transition for transition in transitions if (wf.get_transitions_from(transition.destination) == [] and transition.source and transition.trigger in [MANUAL, SYSTEM] ) ] for transition in terminal_transitions: roles = getRoles(transition) if roles: title = _("revert_transition_title", default="Undo - ${title}", mapping={ "title": translate(transition.title, domain="bungeni") } ) title = "Undo - %s" % transition.title tid = "%s.%s" % (transition.destination, transition.source) pid = "bungeni.%s.wf.%s" % (wf.name, tid) ntransition = Transition(title, transition.destination, transition.source, trigger=MANUAL, condition=allow_retract, permission=pid, condition_args=True, roles=roles) if ntransition.id in wf._transitions_by_id: continue log.debug("adding transition %s", ntransition.id) provideUtility(Permission(pid), IPermission, pid) for role in roles: role_perm_mgr.grantPermissionToRole(pid, role, check=False) transitions.append(ntransition) wf.refresh(wf._states_by_id.values(), transitions)
def add_retract_transitions(wf): def getRoles(transition): original_roles = transition.user_data.get("_roles", []) allowed_roles = set() #transitions to source tx_to_source = wf.get_transitions_to(transition.source) for tx in tx_to_source: if tx.source is None: allowed_roles.update(set(original_roles)) else: for role in tx.user_data.get("_roles", []): if role in original_roles: allowed_roles.add(role) if not len(allowed_roles): allowed_roles = original_roles return allowed_roles transitions = wf._transitions_by_id.values() terminal_transitions = [ transition for transition in transitions if (wf.get_transitions_from(transition.destination) == [] and transition.source and transition.trigger in [MANUAL, SYSTEM]) ] for transition in terminal_transitions: roles = getRoles(transition) if roles: title = _("revert_transition_title", default="Undo - ${title}", mapping={ "title": translate(transition.title, domain="bungeni") }) title = "Undo - %s" % transition.title tid = "%s.%s" % (transition.destination, transition.source) pid = "bungeni.%s.wf.%s" % (wf.name, tid) ntransition = Transition(title, transition.destination, transition.source, trigger=MANUAL, condition=allow_retract, permission=pid, condition_args=True, roles=roles) if ntransition.id in wf._transitions_by_id: continue log.debug("adding transition %s", ntransition.id) provideUtility(Permission(pid), IPermission, pid) for role in roles: role_perm_mgr.grantPermissionToRole(pid, role, check=False) transitions.append(ntransition) wf.refresh(wf._states_by_id.values(), transitions)
def create_special_principals(): auth = queryUtility(IAuthentication) auth.registerPrincipal(User('oms.anonymous')) groot = Group('root') auth.registerPrincipal(groot) root = User('root') root.groups.append('root') auth.registerPrincipal(root) # TODO: create/use a global registry of permissions permissions = ['read', 'modify', 'create', 'add', 'remove', 'delete', 'view', 'traverse', 'zope.Security'] root_role = Role('root', 'root') provideUtility(root_role, IRole, 'root') for perm in permissions: rolePermissionManager.grantPermissionToRole(perm, 'root') principalRoleManager.assignRoleToPrincipal('root', 'root') owner_role = Role('owner', 'o') provideUtility(owner_role, IRole, 'owner') for perm in permissions: rolePermissionManager.grantPermissionToRole(perm, 'owner') for permission in permissions: rolePermissionManager.grantPermissionToRole(permission, 'root') rolePermissionManager.grantPermissionToRole(permission, 'owner') auth.registerPrincipal(User('oms.rest_options')) principalPermissionManager.grantPermissionToPrincipal('rest', 'oms.rest_options')
def create_special_principals(): auth = queryUtility(IAuthentication) auth.registerPrincipal(User('oms.anonymous')) groot = Group('root') auth.registerPrincipal(groot) root = User('root') root.groups.append('root') auth.registerPrincipal(root) # TODO: create/use a global registry of permissions permissions = [ 'read', 'modify', 'create', 'add', 'remove', 'delete', 'view', 'traverse', 'zope.Security' ] root_role = Role('root', 'root') provideUtility(root_role, IRole, 'root') for perm in permissions: rolePermissionManager.grantPermissionToRole(perm, 'root') principalRoleManager.assignRoleToPrincipal('root', 'root') owner_role = Role('owner', 'o') provideUtility(owner_role, IRole, 'owner') for perm in permissions: rolePermissionManager.grantPermissionToRole(perm, 'owner') for permission in permissions: rolePermissionManager.grantPermissionToRole(permission, 'root') rolePermissionManager.grantPermissionToRole(permission, 'owner') auth.registerPrincipal(User('oms.rest_options')) principalPermissionManager.grantPermissionToPrincipal( 'rest', 'oms.rest_options')
def testManyRolesOnePermission(self): perm1 = definePermission('Perm One', 'title').id role1 = defineRole('Role One', 'Role #1').id role2 = defineRole('Role Two', 'Role #2').id roles = manager.getRolesForPermission(perm1) self.assertEqual(len(roles), 0) manager.grantPermissionToRole(perm1, role1) manager.grantPermissionToRole(perm1, role2) manager.grantPermissionToRole(perm1, role2) manager.denyPermissionToRole(perm1, role1) roles = manager.getRolesForPermission(perm1) self.assertEqual(len(roles), 2) self.failIf((role1,Allow) in roles) self.failUnless((role1,Deny) in roles) self.failUnless((role2,Allow) in roles) manager.unsetPermissionFromRole(perm1, role1) roles = manager.getRolesForPermission(perm1) self.assertEqual(len(roles), 1) self.failUnless((role2,Allow) in roles)
def testManyPermissionsOneRole(self): perm1 = definePermission('Perm One', 'P1').id perm2 = definePermission('Perm Two', 'P2').id perm3 = definePermission('Perm Three', 'P3').id role1 = defineRole('Role One', 'Role #1').id perms = manager.getPermissionsForRole(role1) self.assertEqual(len(perms), 0) manager.grantPermissionToRole(perm1, role1) manager.grantPermissionToRole(perm2, role1) manager.grantPermissionToRole(perm2, role1) manager.denyPermissionToRole(perm3, role1) perms = manager.getPermissionsForRole(role1) self.assertEqual(len(perms), 3) self.failUnless((perm1,Allow) in perms) self.failUnless((perm2,Allow) in perms) self.failUnless((perm3,Deny) in perms) manager.unsetPermissionFromRole(perm1, role1) perms = manager.getPermissionsForRole(role1) self.assertEqual(len(perms), 2) self.failUnless((perm2,Allow) in perms)
def _load(workflow_name, workflow): """ (workflow_name:str, workflow:etree_doc) -> Workflow """ workflow_title = xas(workflow, "title") naming.MSGIDS.add(workflow_title) workflow_description = xas(workflow, "description") naming.MSGIDS.add(workflow_description) transitions = [] states = [] note = xas(workflow, "note") # initial_state, in XML indicated with a transition.@source="" initial_state = None ZCML_PROCESSED = bool(workflow_name in ZCML_WORKFLOWS_PROCESSED) if not ZCML_PROCESSED: ZCML_WORKFLOWS_PROCESSED.add(workflow_name) ZCML_LINES.append(ZCML_INDENT) ZCML_LINES.append(ZCML_INDENT) ZCML_LINES.append("%s<!-- %s -->" % (ZCML_INDENT, workflow_name)) def validate_id(id, tag): """Ensure that ID values are unique within the same XML doc scope. """ m = "Invalid element <%s> id=%r in workflow %r" % (tag, id, workflow_name) assert id not in validate_id.wuids, "%s -- id not unique in workflow document" % (m) validate_id.wuids.add(id) validate_id.wuids = set() # unique IDs in this XML workflow file def get_from_state(state_id): if state_id is None: return for state in states: if state.id == state_id: return state raise ValueError("Invalid value: permissions_from_state=%r" % (state_id)) def check_not_global_grant(pid, role): # ensure global and local assignments are distinct # (global: global_pid_roles, workflow_name) global_proles = global_pid_roles.get(pid, "") assert role not in global_proles, ("Workflow [%s] may not mix " "global and local granting of a same permission [%s] to a " "same role [%s].") % (workflow_name, pid, role) # permission_actions -> permissions for this type for (key, permission_action) in capi.schema.qualified_permission_actions( workflow_name, xas(workflow, "permission_actions", "").split() ): pid = "bungeni.%s.%s" % (key, permission_action) title = "%s %s" % ( permission_action, naming.split_camel(naming.model_name(key))) # !+ add to a Workflow.defines_permissions list ZCML_LINES.append( '%s<permission id="%s" title="%s" />' % (ZCML_INDENT, pid, title)) provideUtility(Permission(pid), IPermission, pid) # global grants global_pid_roles = {} # {pid: [role]} global_grants = get_permissions_from_allows(workflow_name, workflow) for (setting, pid, role) in global_grants: # for each global permission, build list of roles it is set to global_pid_roles.setdefault(pid, []).append(role) assert setting and pid and role, \ "Global grant must specify valid permission/role" #!+RNC # !+ add to a Workflow.global_grants list ZCML_LINES.append( '%s<grant permission="%s" role="%s" />' % (ZCML_INDENT, pid, role)) # no real need to check that the permission and role of a global grant # are properly registered in the system -- an error should be raised # by the zcml if either is not defined. rpm.grantPermissionToRole(pid, role, check=False) for perm, roles in global_pid_roles.items(): # assert roles mix limitations for state permissions assert_distinct_permission_scopes(perm, roles, workflow_name, "global grants") # all workflow features (enabled AND disabled) workflow_features = load_features(workflow_name, workflow) enabled_feature_names = [None] + [ f.name for f in workflow_features if f.enabled ] # !+EVENT_FEATURE_TYPES add each as enabled_feature_names, or extend the # facet quailifer by feature_name(sub_type_key).facet_ref? # workflow facets workflow_facets = load_facets(workflow_name, workflow) # states for s in workflow.iterchildren("state"): # @id state_id = xas(s, "id") assert state_id, "Workflow State must define @id" #!+RNC validate_id(state_id, "state") # @actions - transition-to-state actions state_actions = [] for action_name in xas(s, "actions", "").split(): state_actions.append(capi.get_workflow_action(action_name)) # @permissions_from_state permissions = [] # [ tuple(bool:int, permission:str, role:str) ] # state.@permissions_from_state : to reduce repetition and enhance # maintainibility of workflow XML files, a state may inherit ALL # permissions defined by the specified state. NO other permissions # may be specified by this state. from_state = get_from_state(xas(s, "permissions_from_state")) parent_permissions = xab(s, "parent_permissions") if parent_permissions: pass # no own permission definitions allowed elif from_state: # assimilate (no more no less) the state's permissions !+tuple, use same? permissions[:] = from_state.permissions else: used_facets_fq = resolve_state_facets( workflow_name, workflow_facets, enabled_feature_names, s) # assimilate permissions from facets from None and all enabled features def add_facet_permissions(facet): for perm in facet.permissions: check_add_assign_permission(workflow_name, permissions, perm) check_not_global_grant(perm[1], perm[2]) for (feature_name, qtk) in used_facets_fq: # debug check that feature is enabled assert feature_name in enabled_feature_names facet = used_facets_fq[(feature_name, qtk)] if facet is not None: add_facet_permissions(facet) # states state_title = xas(s, "title") naming.MSGIDS.add(state_title) states.append( State(state_id, state_title, xas(s, "note"), state_actions, permissions, parent_permissions, xab(s, "obsolete"), ) ) STATE_IDS = [ s.id for s in states ] # transitions for t in workflow.iterchildren("transition"): title = xas(t, "title") naming.MSGIDS.add(title) # sources, empty string -> initial_state sources = t.get("source").split() or [initial_state] assert len(sources) == len(set(sources)), \ "Transition contains duplicate sources [%s]" % (sources) for source in sources: if source is not initial_state: assert source in STATE_IDS, \ "Unknown transition source state [%s]" % (source) # destination must be a valid state destination = t.get("destination") assert destination in STATE_IDS, \ "Unknown transition destination state [%s]" % (destination) # optionals -- only set on kw IFF explicitly defined kw = {} for i in TRANS_ATTRS_OPTIONALS: val = xas(t, i) if not val: # we let setting of defaults be handled downstream continue kw[i] = val # data up-typing # # trigger if "trigger" in kw: kw["trigger"] = trigger_value_map[kw["trigger"]] # roles -> permission - one-to-one per transition roles = capi.schema.qualified_roles(kw.pop("roles", "")) if not is_zcml_permissionable(t): assert not roles, "Workflow [%s] - non-permissionable transition " \ "does not allow @roles [%s]." % (workflow_name, roles) #!+RNC kw["permission"] = None # None -> CheckerPublic # !+CAN_EDIT_AS_DEFAULT_TRANSITION_PERMISSION(mr, oct-2011) this feature # is functional (uncomment following elif clause) but as yet not enabled. # # Advantage would be that it would be easier to keep transitions # permissions in sync with object permissions (set in state) as the # majority of transition require exactly this as privilege; for the # occassional transition needing a different privilege, the current # transition.@roles mechanism may be used to make this explicit. # # Need to consider implications further; the zope_principal_role_map db # table, that caches contextual roles for principals, should probably # first be reworked to be db-less (as for zope_role_permission_map). # #elif not roles: # # then as fallback transition permission use can modify object # kw["permission"] = "bungeni.%s.Edit" % (workflow_name) # fallback permission else: # Dedicated permission for XML multi-source transition. # Given that, irrespective of how sources are grouped into # multi-source XML <transition> elements, there may be only *one* # path from any given *source* to any given *destination* state, # it suffices to use only the first source element + the destination # to guarantee a unique identifier for an XML transition element. # # Note: the "-" char is not allowed within a permission id # (so we use "." also here). # tid = "%s.%s" % (sources[0] or "", destination) pid = "bungeni.%s.wf.%s" % (workflow_name, tid) if not ZCML_PROCESSED: zcml_transition_permission(pid, title, roles) # remember list of roles from xml kw["_roles"] = roles kw["permission"] = pid # python resolvables if "condition" in kw: kw["condition"] = capi.get_workflow_condition(kw["condition"]) # numeric if "order" in kw: kw["order"] = float(kw["order"]) # ValueError if not numeric # bool if "require_confirmation" in kw: try: kw["require_confirmation"] = misc.as_bool(kw["require_confirmation"]) assert kw["require_confirmation"] is not None #!+RNC except: raise ValueError("Invalid transition value " '[require_confirmation="%s"]' % ( t.get("require_confirmation"))) # multiple-source transitions are really multiple "transition paths" for source in sources: args = (title, source, destination) transitions.append(Transition(*args, **kw)) log.debug("[%s] adding transition [%s-%s] [%s]" % ( workflow_name, source or "", destination, kw)) return Workflow(workflow_name, workflow_features, workflow_facets, states, transitions, global_grants, workflow_title, workflow_description, note)