예제 #1
0
    def test_merge_subscribe_multiple_times(self):
        """
            Given a subscribe previous action set
            And an subscribe new action set
            And a third subscribe action set
            When i merge the three sets
            I get a copy also with subscribe set
        """

        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'subscribe': True}, {'subscribe': True})
        actions = merge_actions(actions, {'subscribe': True})

        self.assertItemsEqual(actions, ['subscribe'])
예제 #2
0
    def test_merge_grants_multiple_times(self):
        """
            Given a grants previous action set
            And an grant new action set
            And a third grant action set
            When i merge the three sets
            I get a copy with grants union of all sets
            And without duplicated grants
        """

        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'grant': ['write', 'read']},
                                {'grant': ['read']})
        actions = merge_actions(actions, {'grant': ['flag']})

        self.assertItemsEqual(actions, ['grant'])
        self.assertItemsEqual(actions['grant'], ['read', 'write', 'flag'])
예제 #3
0
    def test_merge_revokes_multiple_times_preserve_grants(self):
        """
            Given a revoke previous action set
            And an revoke new action set
            And a third grant action set
            When I merge the three sets
            Then I get a copy with revokes intersection of all sets
            And I get a copy with grants unions of all sets
            And only revokes also present in grants dissappear
        """

        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'revoke': ['write', 'read']},
                                {'revoke': ['read']})
        actions = merge_actions(actions, {'grant': ['read']})

        self.assertItemsEqual(actions, ['grant'])
        self.assertItemsEqual(actions['grant'], ['read'])
예제 #4
0
    def test_merge_grants_multiple_times_preserve_grants(self):
        """
            Given a grant previous action set
            And an grant new action set
            And a third revoke action set
            When I merge the three sets
            Then I get a copy with revokes intersection of all sets
            And I get a copy with grants unions of all sets
            And revokes from third set won't remove grants
        """

        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(None, {'grant': ['write'], 'revoke': ['read']})
        actions = merge_actions(actions, {
            'grant': ['read'],
            'revoke': ['read']
        })

        self.assertItemsEqual(actions, ['grant'])
        self.assertItemsEqual(actions['grant'], ['write', 'read'])
예제 #5
0
    def test_merge_revokes_multiple_times(self):
        """
            Given a revoke previous action set
            And an revoke new action set
            And a third revoke action set
            When i merge the three sets
            Then I get a copy with revokes intersection of all sets
            And only revokes present in all sets will remain
        """

        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'revoke': ['write', 'read']},
                                {'revoke': ['read']})

        self.assertItemsEqual(actions, ['revoke'])
        self.assertItemsEqual(actions['revoke'], ['read'])

        actions = merge_actions(actions, {'revoke': ['flag']})

        self.assertItemsEqual(actions, [])
예제 #6
0
    def test_merge_revokes_with_empty(self):
        """
            Given a revoke previous action set
            And an empty new action set
            When i merge the two sets
            The revokes go away
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'revoke': ['write']}, {})

        self.assertItemsEqual(actions, [])
예제 #7
0
    def test_merge_subscribe_with_empty(self):
        """
            Given an subscribe previous action set
            And an empty new action set
            When i merge the two sets
            I get a copy of the former
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'subscribe': True}, {})

        self.assertItemsEqual(actions, ['subscribe'])
예제 #8
0
    def test_merge_empty_with_subscribe(self):
        """
            Given an empty initial actions set
            And an subscribe new action set
            When i merge the two sets
            I get a copy of the latter
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(None, {'subscribe': True})

        self.assertItemsEqual(actions, ['subscribe'])
예제 #9
0
    def test_merge_empty_with_empty(self):
        """
            Given an empty initial actions set
            And an empty new action set
            When i merge the two sets
            I get an empty action set
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(None, {})

        self.assertFalse(actions)
예제 #10
0
    def test_merge_grants_with_empty(self):
        """
            Given a grant previous action set
            And an empty new action set
            When i merge the two sets
            I get a copy of the former
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions({'grant': ['write']}, {})

        self.assertItemsEqual(actions, ['grant'])
        self.assertItemsEqual(actions['grant'], ['write'])
예제 #11
0
    def test_merge_empty_with_revokes(self):
        """
            Given an empty initial actions set
            And a revoke new action set
            When i merge the two sets
            I get a copy of the latter
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(None, {'revoke': ['write']})

        self.assertItemsEqual(actions, ['revoke'])
        self.assertItemsEqual(actions['revoke'], ['write'])
예제 #12
0
    def test_merge_empty_with_all(self):
        """
            Given an empty initial actions set
            And a subscribe + grant + revoke new action set
            When i merge the two sets
            I get a copy of the latter
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(None, {
            'revoke': ['write'],
            'grant': ['read'],
            'subscribe': True
        })

        self.assertItemsEqual(actions, ['revoke', 'grant', 'subscribe'])
        self.assertItemsEqual(actions['revoke'], ['write'])
        self.assertItemsEqual(actions['grant'], ['read'])
예제 #13
0
    def test_merge_all_with_empty(self):
        """
            Given a subscribe + grant + revoke previous action set
            And an empty new action set
            When i merge the two sets
            I get a copy of the latter
            And the revokes are gone
        """
        from ulearnhub.models.utils import merge_actions

        actions = merge_actions(
            {
                'revoke': ['write'],
                'grant': ['read'],
                'subscribe': True
            }, {})

        self.assertItemsEqual(actions, ['grant', 'subscribe'])
        self.assertItemsEqual(actions['grant'], ['read'])
예제 #14
0
    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 {}