def test_action_generation_subscribed(self): """ Given a set of wanted permissions And an existent subcription If we currently have all wanted permissions Then no action is generated """ from ulearnhub.models.utils import generate_actions subscription = {'permissions': ['read', 'write']} policy_granted_permissions = set(['read', 'write']) wanted_permissions = set(['read', 'write']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), [])
def test_action_generation_subscribed_triggers_grant(self): """ Given a set of wanted permissions And an existent subcription If there are some wanted permissions that we don't have Then a grant action is generated """ from ulearnhub.models.utils import generate_actions subscription = {'permissions': ['read']} policy_granted_permissions = set(['read']) wanted_permissions = set(['read', 'write']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), ['grant']) self.assertItemsEqual(actions['grant'], ['write'])
def test_action_generation_new_subscription(self): """ Given a set of wanted permissions And an inexistent subcription If all wanted permissions are in policy Then only subscription action is generated """ from ulearnhub.models.utils import generate_actions subscription = {} policy_granted_permissions = set(['write', 'read']) wanted_permissions = set(['write', 'read']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), ['subscribe']) self.assertTrue(actions['subscribe'])
def test_action_generation_subscribed_triggers_revoke(self): """ Given a set of wanted permissions And an existent subcription If we have some permissions that we must not have And that permissions has been granted Then a revoke action is generated """ from ulearnhub.models.utils import generate_actions subscription = {'permissions': ['read', 'write']} policy_granted_permissions = set(['read', 'write']) wanted_permissions = set(['read']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), ['revoke']) self.assertItemsEqual(actions['revoke'], ['write'])
def test_action_generation_subscribed_triggers_revoke_and_grant(self): """ Given a set of wanted permissions And an existent subcription If there are some wanted permissions not in current permissions If there are some not wanted permissions in current_permissions Then a subscription action is generated And a revoke action is generated And a grant action is generated """ from ulearnhub.models.utils import generate_actions subscription = {'permissions': ['read', 'write']} policy_granted_permissions = set(['write']) wanted_permissions = set(['read', 'flag']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), ['revoke', 'grant']) self.assertItemsEqual(actions['revoke'], ['write']) self.assertItemsEqual(actions['grant'], ['flag'])
def test_action_generation_new_subscription_triggers_grant_and_revoke( self): """ Given a set of wanted permissions And an inexistent subcription If there are some wanted permissions not in policy If there are some not wanted permissions in policy Then a subscription action is generated And a revoke action is generated And a grant action is generated """ from ulearnhub.models.utils import generate_actions subscription = {} policy_granted_permissions = set(['write']) wanted_permissions = set(['read']) actions = generate_actions(subscription, policy_granted_permissions, wanted_permissions) self.assertItemsEqual(actions.keys(), ['subscribe', 'revoke', 'grant']) self.assertTrue(actions['subscribe']) self.assertItemsEqual(actions['revoke'], ['write']) self.assertItemsEqual(actions['grant'], ['read'])
def handle_rabbitmq(self, request, *args, **kwargs): """ Update a context's users ACLS Decomposes given groups into a list of users. With that list of users generates the minimum set of tasks (SUB, UNSUB, REVOKE, GRANT) to sync the community status to max context and subscriptions. Each of the final tasks generated is feeded to rabbitmq to be processed asynchronously. Format of request to this service is as follows: { "component": { "type": "communities", "id": "url" } "context": "http://(...)", "acl": { "groups": [ {"id": , "role": ""}, ... ] "users": [ {"id": , "role": ""}, ] }, "permission_mapping": { "reader": ['read'], "writer": ['read', 'write'], "owner": ['read', 'write'] }, "ignore_grants_and_vetos": true, } component: type and id of the component that's triggering this call context: The context on which the syncacl tasks will be performed acl.groups: list of group acls to process. acl.users: list of user acls to process. acl.*: Each acl entry has an id identifing the group/user (cn) and a role permission_mapping: For each role in acls, there must be a list of permissions that a user with that role must have in its max subscription ignore_grants_and_vetos: if true, peristent grants and vetoes on context subscriptions will be overriden so that the final subscription state matches the requested state. This is the default behaviour. """ data = request.json domain = self.__parent__ context_url = data['context'] # Get required components for this service maxserver = domain.get_component(MaxServer) ldapserver = domain.get_component(LdapServer) rabbitserver = domain.get_component(RabbitServer) # Get target context and all of its subscriptions maxclient = maxserver.maxclient authenticated_username, authenticated_token, scope = request.auth_headers maxclient.setActor(authenticated_username) maxclient.setToken(authenticated_token) policy_granted_permissions, subscriptions = get_context_data( maxclient, context_url) acl_groups = data['acl'].get('groups', []) acl_users = data['acl'].get('users', []) permission_mapping = data['permission_mapping'] def expanded_users(): """ Returns iterator with groups in request expanded to get all individual users. Transform each user to mimic entries in requests's ['acl']['users'], picking the role specified in the group. Preserve group for further checks. """ if not acl_groups: raise StopIteration() ldapserver.server.connect() for group in acl_groups: users = ldapserver.server.get_group_users( group['id'].encode('utf-8')) for username in users: yield { 'id': username, 'role': group['role'], 'group': group['id'] } # Disconnect ldap server, we won't need it outside here ldapserver.server.disconnect() # To keep track of overwrites caused by user duplication # At the end of the process, subscribed user NOT IN target users # will be unsubscribed actions_by_user = {} # Iterate over group users and single users acl's at once for user in chain(expanded_users(), acl_users): username = user['id'] role = user['role'] wanted_permissions = set(permission_mapping.get(role, [])) # Get the previous defined actions on this user, if any actions = actions_by_user.get(username, None) # Generate actions based on current permissions, policy, and wanted permissions new_actions = generate_actions(subscriptions.get(username, {}), policy_granted_permissions, wanted_permissions) # Merge new actions into previous actions, preserving the most beneficient actions = merge_actions(actions, new_actions) # Store user to track overwrites actions_by_user[username] = actions client = rabbitserver.notifications # All the users present in subscription and not in the ACL's will be unsubscribed missing_users = set(subscriptions.keys()) - set(actions_by_user.keys()) for username in missing_users: client.sync_acl(domain.name, context_url, username, {"unsubscribe": True}) gevent.sleep() for username, actions in actions_by_user.items(): if actions: client.sync_acl(domain.name, context_url, username, actions) gevent.sleep() gevent.sleep(0.1) return {}