def test_user_status(self): try: User.provision_user(service_accounts['user']['client_email']) except FusilladeHTTPException: pass user_id = service_accounts['user']['client_email'] user_headers = {'Content-Type': "application/json"} user_headers.update(get_auth_header(service_accounts['user'])) admin_headers = {'Content-Type': "application/json"} admin_headers.update(get_auth_header(service_accounts['admin'])) disable_url = furl(f"/v1/user/{user_id}", query_params={'user_id': user_id, 'status': 'disabled'}) enable_url = furl(f"/v1/user/{user_id}", query_params={'user_id': user_id, 'status': 'enabled'}) test_user_url = furl(f"/v1/user/{user_id}/") # check user can get info resp = self.app.get(test_user_url.url, headers=user_headers) self.assertEqual(200, resp.status_code) # disable the user resp = self.app.put(disable_url.url, headers=admin_headers) self.assertEqual(200, resp.status_code) # verify that user cannot access things resp = self.app.get(test_user_url.url, headers=user_headers) self.assertEqual(403, resp.status_code) # enable user resp = self.app.put(enable_url.url, headers=admin_headers) self.assertEqual(200, resp.status_code) # verify that user has access resp = self.app.get(test_user_url.url, headers=user_headers) self.assertEqual(200, resp.status_code)
def assert_authorized(user, actions, resources, context_entries=None): """ Asserts a user has permission to perform actions on resources. :param user: :param actions: :param resources: :param context_entries: :return: """ u = User(user) context_entries = context_entries if context_entries else [] try: authz_params = u.get_authz_params() except AuthorizationException: raise FusilladeForbiddenException(detail="User must be enabled to make authenticated requests.") else: context_entries.extend(restricted_context_entries(authz_params)) if not evaluate_policy( user, actions, resources, authz_params['IAMPolicy'], context_entries=context_entries)['result']: logger.info(dict(message="User not authorized.", user=u._path_name, action=actions, resources=resources)) raise FusilladeForbiddenException() else: logger.info(dict(message="User authorized.", user=u._path_name, action=actions, resources=resources))
def get_resource_authz_parameters(user: str, resources: Union[List[str], str]): """ Get all policy ids, and send them to lookup policy, group them by policy type. :param user: :param resource: :return: """ policies = [] _user = User(user) try: groups = _user.groups except cd_client.exceptions.ResourceNotFoundException: _user = User.provision_user(user) groups = _user.groups # Only support a single resource for now resource = resources[0] if isinstance(resources, list) else resources r_type, r_id, *_ = resource.split(':')[-1].split('/') if r_type in ResourceType.get_types(): r_id = ResourceId(r_type, r_id) resource_policies = r_id.check_access( [_user] + [Group(object_ref=g) for g in groups]) if not resource_policies: raise ResourceNotFound("ResourceNotFound") policies.extend(resource_policies) policies.extend(list(_user.get_policy_ids())) authz_params = Config.get_directory().get_policies(policies) if authz_params.get('ResourcePolicy'): authz_params['ResourcePolicy'] = combine( [i['policy_document'] for i in authz_params.get('ResourcePolicy')]) return authz_params
def test_get_group_users(self): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) name = "test_get_group_users_group" key = 'users' group = Group.create(name) resp = self.app.get(f'/v1/group/{name}/users', headers=headers) group_user_names = [User(user).name for user in group.get_users_iter()] self.assertEqual(0, len(json.loads(resp.body)[key])) users = [User.provision_user(f"test_get_group_user_{i}", groups=[name]).name for i in range(10)] self._test_paging(f'/v1/group/{name}/users', headers, 5, key)
def backup_users(): users = [] for name in list_node(User, 'users'): user = User(name) info = { 'name': user.name, 'status': user.status, 'policies': format_policies([(p, user.get_policy(p)) for p in user.allowed_policy_types]), 'roles': [Role(object_ref=r).name for r in user.roles] } users.append(info) print("USERS:", *users, sep='\n\t') return users
def test_get_user(self): try: User.provision_user(service_accounts['user']['client_email']) except FusilladeHTTPException: pass headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['user'])) name = service_accounts['user']['client_email'] resp = self.app.get(f'/v1/user/[email protected]', headers=headers) self.assertEqual(403, resp.status_code) resp = self.app.get(f'/v1/user/{name}/', headers=headers) resp.raise_for_status() self.assertEqual(name, json.loads(resp.body)['user_id'])
def userinfo(token_info): """ Part of OIDC """ from fusillade.directory import User, Group, Role user = User(token_info['email']) # TODO save user info in fusillade at the same time. token_info[f"https://{os.environ['API_DOMAIN_NAME']}/app_metadata"] = { 'authorization': { 'groups': Group.get_names(user.groups), 'roles': Role.get_names(user.roles), 'scope': [i for i in user.get_actions()] } } return make_response(json.jsonify(**token_info), requests.codes.ok)
def test_delete_group(self): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) role = Role.create("test_delete_group_role").name user = User.provision_user("test_delete_group_user").name policy = create_test_IAMPolicy("test_delete_group_policy") group_id = "test_delete_group_group" with self.subTest("Group delete with users and roles."): resp = self.app.post(f'/v1/group', headers=headers, data=json.dumps({ "group_id": group_id, "roles": [role], "policy": policy })) resp.raise_for_status() resp = self.app.put(f'/v1/user/{user}/groups?action=add', headers=headers, data=json.dumps({"groups": [group_id]})) resp.raise_for_status() resp = self.app.delete(f'/v1/group/{group_id}', headers=headers) self.assertEqual(resp.status_code, 200) resp = self.app.get(f'/v1/user/{user}/groups', headers=headers) groups = json.loads(resp.body)['groups'] self.assertNotIn(group_id, groups) with self.subTest("delete a group that does not exist."): resp = self.app.delete(f'/v1/group/{group_id}', headers=headers) self.assertEqual(resp.status_code, 404)
def test_remove_groups(self): name = "*****@*****.**" test_groups = [(f"group_{i}", create_test_IAMPolicy(f"GroupPolicy{i}")) for i in range(5)] groups = [Group.create(*i).name for i in test_groups] user = User.provision_user(name) with self.subTest( "A user is removed from a group when remove_group is called for a group the user belongs " "to."): user.add_groups(groups) self.assertEqual(len(user.groups), 6) user.remove_groups(groups) self.assertEqual(len(user.groups), 1) with self.subTest( "Error is raised when removing a user from a group it's not in." ): self.assertRaises(cd_client.exceptions.BatchWriteException, user.remove_groups, groups) self.assertEqual(len(user.groups), 1) with self.subTest( "An error is raised and the user is not removed from any groups when the user is in some of " "the groups to remove."): user.add_groups(groups[:2]) self.assertEqual(len(user.groups), 3) self.assertRaises(cd_client.exceptions.BatchWriteException, user.remove_groups, groups) self.assertEqual(len(user.groups), 3)
def test_set_policy(self): name = "*****@*****.**" user = User.provision_user(name) with self.subTest( "The initial user policy is None, when the user is first created" ): self.assertFalse(user.get_policy()) statement = create_test_IAMPolicy(f"UserPolicySomethingElse") user.set_policy(statement) with self.subTest( "The user policy is set when statement setter is used."): expected_statement = statement self.assertJSONEqual(user.get_policy(), expected_statement) self.assertJSONIn(expected_statement, [ p['policy_document'] for p in user.get_authz_params()['IAMPolicy'] ]) statement = create_test_IAMPolicy(f"UserPolicySomethingElse2") user.set_policy(statement) with self.subTest("The user policy changes when set_policy is used."): expected_statement = statement self.assertJSONEqual(user.get_policy(), expected_statement) self.assertJSONIn(expected_statement, [ p['policy_document'] for p in user.get_authz_params()['IAMPolicy'] ]) with self.subTest( "Error raised when setting policy to an invalid statement"): with self.assertRaises(FusilladeHTTPException): user.set_policy({"Statement": "Something else"}) self.assertJSONEqual(user.get_policy(), expected_statement)
def test_put_user_id(self): tests = [ { 'name': "*****@*****.**", 'status': 'enabled', 'response': { 'code': 200 } }, { 'name': "*****@*****.**", 'status': 'disabled', 'response': { 'code': 200 } } ] for test in tests: with self.subTest(test["name"]): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) url = furl(f'/v1/user/{test["name"]}') query_params = { 'user_id': test['name'], 'status': test['status'] } url.add(query_params=query_params) user = User.provision_user(test['name']) if test['status'] == 'disabled': user.enable() resp = self.app.put(url.url, headers=headers) self.assertEqual(test['response']['code'], resp.status_code)
def test_resource_id(self): resource_type = 'test_type' test_type = self._create_resource_type(resource_type) # add an access policy test_type.create_policy( 'Reader', create_test_ResourcePolicy("resource policy", ['readproject']), 'ResourcePolicy') test_id = test_type.create_id('ABCD') # list ids test_types = test_type.list_ids() self.assertTrue(test_types) # give a user read access to the id user = [User('public')] test_id.add_principals(user, 'Reader') self.assertEqual( test_id.check_access(user)[0], test_type.get_policy_path('Reader')) # update access test_type.create_policy( 'RW', create_test_ResourcePolicy("resource policy", ['readproject', 'writeproject']), 'ResourcePolicy') test_id.update_principal(user[0], 'RW') self.assertEqual( test_id.check_access(user)[0], test_type.get_policy_path('RW')) # remove access test_id.remove_principals(user) self.assertFalse(test_id.check_access(user)) # multiple principals users = [User.provision_user(f'user{i}') for i in range(3)] test_id.add_principals(users[:-1], 'Reader') self.assertEqual(test_id.check_access(users), [test_type.get_policy_path('Reader')]) # get policies policies = test_id.get_resource_policy(users) self.assertTrue(test_type.get_policy('Reader'))
def setUpClass(cls): if not is_integration(): new_test_directory() try: User.provision_user(service_accounts['admin']['client_email'], roles=['fusillade_admin']) except Exception: pass if is_integration(): from tests.infra.integration_server import IntegrationTestHarness cls.app = IntegrationTestHarness() else: from tests.infra.server import ChaliceTestHarness # ChaliceTestHarness must be imported after FUSILLADE_DIR has be set cls.app = ChaliceTestHarness() cls._save_state()
def test_get_username_groups(self): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) name = "*****@*****.**" key = 'groups' user = User.provision_user(name) resp = self.app.get(f'/v1/user/{name}/groups', headers=headers) self.assertEqual(1, len(json.loads(resp.body)[key])) groups = [Group.create(f"test_get_username_groups_{i}").name for i in range(8)] user.add_groups(groups) self._test_paging(f'/v1/user/{name}/groups', headers, 5, key)
def test_get_groups(self): name = "*****@*****.**" test_groups = [(f"group_{i}", create_test_IAMPolicy(f"GroupPolicy{i}")) for i in range(5)] groups = [Group.create(*i) for i in test_groups] user = User.provision_user(name) with self.subTest( "A user is in the public group when user is first created."): self.assertEqual( Group(object_ref=user.groups[0]).name, 'user_default') user.add_groups([]) with self.subTest( "A user is added to no groups when add_groups is called with no groups" ): self.assertEqual(len(user.groups), 1) with self.subTest( "An error is returned when add a user to a group that does not exist." ): with self.assertRaises( cd_client.exceptions.BatchWriteException) as ex: user.add_groups(["ghost_group"]) self.assertTrue('ResourceNotFoundException' in ex.exception.response['Error']['Message']) self.assertEqual(len(user.groups), 1) with self.subTest( "An error is returned when add a user to a group that they are already apart." ): with self.assertRaises( cd_client.exceptions.BatchWriteException) as ex: user.add_groups(["user_default"]) self.assertTrue('InvalidAttachmentException' in ex.exception.response['Error']['Message']) self.assertEqual(len(user.groups), 1) user.add_groups([group.name for group in groups]) with self.subTest( "A user is added to multiple groups when add_groups is called with multiple groups" ): self.assertEqual(len(user.groups), 6) with self.subTest( "A user inherits the groups policies when joining a group"): policies = set([ normalize_json(p['policy_document']) for p in user.get_authz_params()['IAMPolicy'] ]) expected_policies = set( [normalize_json(i[1]) for i in test_groups]) expected_policies.update(self.default_user_policies) self.assertSetEqual(policies, expected_policies)
def test_get_username_roles(self): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) name = "*****@*****.**" key = 'roles' user = User.provision_user(name) resp = self.app.get(f'/v1/user/{name}/roles', headers=headers) user_role_names = [Role(None, role).name for role in user.roles] self.assertEqual(0, len(json.loads(resp.body)[key])) roles = [Role.create(f"test_get_username_role_{i}").name for i in range(11)] user.add_roles(roles) self._test_paging(f'/v1/user/{name}/roles', headers, 6, key)
def _modify_users(cloud_node, request): action = request.args['action'] resp = { 'users': request.json['users'], 'action': action, f'{cloud_node.object_type}_id': cloud_node.name } try: User.exists(request.json['users']) if action == 'add': cloud_node.add_users(request.json['users']) elif action == 'remove': cloud_node.remove_users(request.json['users']) except cd_client.exceptions.BatchWriteException as ex: resp['msg'] = ex.response['Error']['Message'] code = 304 else: resp[ 'msg'] = f"{cloud_node.object_type}'s users successfully modified." code = 200 return resp, code
def test_users(self): emails = ["*****@*****.**", "*****@*****.**", "*****@*****.**"] users = [User.provision_user(email).name for email in emails] with self.subTest( "A user is added to the group when add_users is called"): group = Group.create("test") user = User.provision_user("*****@*****.**") group.add_users([user.name]) actual_users = [i for i in group.get_users_iter()] self.assertEqual(len(actual_users), 1) self.assertEqual(User(object_ref=actual_users[0]).name, user.name) with self.subTest( "Multiple users are added to the group when multiple users are passed to add_users" ): group = Group.create("test2") group.add_users(users) actual_users = [i[1] for i in group.get_users_iter()] self.assertEqual(len(actual_users), 3) with self.subTest( "Error returned when a user is added to a group it's already apart of." ): group = Group.create("test3") group.add_users(users) try: group.add_users(users) except cd_client.exceptions.BatchWriteException: pass actual_users = [i[1] for i in group.get_users_iter()] self.assertEqual(len(actual_users), 3) with self.subTest( "Error returned when adding a user that does not exist"): group = Group.create("test4") user = User("*****@*****.**") try: group.add_users([user.name]) except cd_client.exceptions.ResourceNotFoundException: pass
def test_ownership(self): user = User.provision_user('test_user') group = Group.create("group_ownership") with self.subTest("A user is not an owner when new group is created"): self.assertFalse(user.is_owner(group)) user.add_ownership(group) with self.subTest("A user is owner after assigning ownership."): self.assertTrue(user.is_owner(group)) group2 = Group.create("group_ownership2") user.add_ownership(group2) with self.subTest("List groups owned, when list_owned is called"): resp = user.list_owned(Group) user.remove_ownership(group) self.assertFalse(user.is_owner(group))
def test_status(self): name = "*****@*****.**" user = User.provision_user(name) with self.subTest("A user's status is enabled when provisioned."): self.assertEqual(user.status, 'enabled') with self.subTest( "A user's status is disabled when user.disable is called."): user.disable() self.assertEqual(user.status, 'disabled') with self.subTest( "A user's status is enabled when user.enable is called."): user.enable() self.assertEqual(user.status, 'enabled')
def test_user_owned(self): headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) name = "*****@*****.**" key = 'roles' user = User.provision_user(name) url = furl(f"/v1/user/{name}/owns", query_params={'resource_type': 'role'}).url resp = self.app.request(url, headers=headers) self.assertEqual('False', resp.headers['X-OpenAPI-Pagination']) user_role_names = [Role(object_ref=role).name for role in user.roles] roles = [Role.create(f"test_user_owned_role_{i}") for i in range(11)] user.add_roles([role.name for role in roles]) [user.add_ownership(role) for role in roles] self._test_paging(url, headers, 6, key)
def backup_groups(): groups = [] for name in list_node(Group, 'groups'): group = Group(name) info = { 'name': group.name, 'members': [User(object_ref=u).name for u in group.get_users_iter()], 'policies': format_policies([(p, group.get_policy(p)) for p in group.allowed_policy_types]), 'owners': group.list_owners(), 'roles': [Role(object_ref=r).name for r in group.roles] } groups.append(info) print("GROUPS:", *groups, sep='\n\t') return groups
def test_eval(self): actions = ['test:readproject', 'test:writeproject', 'test:deleteproject'] resource_type = 'test_type' test_type = ResourceType.create(resource_type, actions) access_level = 'Reader' resource_policy = { "Version": "2012-10-17", "Statement": [ { "Principal": "*", "Sid": "project_reader", "Effect": "Allow", "Action": ['test:readproject'], "Resource": f"{self.arn_prefix}{resource_type}/*" } ] } test_type.create_policy(access_level, resource_policy, 'ResourcePolicy') user = User.provision_user('test_user') type_id = '1234455' resource = f'{self.arn_prefix}{resource_type}/{type_id}' with self.subTest("A user does not have access when they only have permitting resource_policy and no " "permitting IAMPolicy"): test_id = test_type.create_id(type_id) test_id.add_principals([user], access_level) x = get_resource_authz_parameters(user.name, resource) resp = evaluate_policy(user.name, ['test:readproject'], [resource], x['IAMPolicy'], x['ResourcePolicy']) self.assertFalse(resp['result']) with self.subTest("A user has a access when they have a permitting resource policy and IAMPolicy"): IAMpolicy = { "Statement": [ { "Sid": "project_reader", "Effect": "Allow", "Action": [ "test:readproject" ], "Resource": f"{self.arn_prefix}{resource_type}/*" } ] } role = Role.create('project_reader', IAMpolicy) user.add_roles([role.name]) x = get_resource_authz_parameters(user.name, resource) resp = evaluate_policy(user.name, ['test:readproject'], [resource], x['IAMPolicy'], x['ResourcePolicy']) self.assertTrue(resp['result'])
def test_get_authz_params(self): actions = ['test:readproject', 'test:writeproject', 'test:deleteproject'] resource_type = 'test_type' test_type = ResourceType.create(resource_type, actions) access_level = 'Reader' resource_policy = { "Version": "2012-10-17", "Statement": [ { "Principal": "*", "Sid": "project_reader", "Effect": "Allow", "Action": ['test:readproject'], "Resource": f"{self.arn_prefix}{resource_type}/*" } ] } test_type.create_policy(access_level, resource_policy, 'ResourcePolicy') user = User.provision_user('test_user') type_id = '1234455' resource = f'{self.arn_prefix}{resource_type}/{type_id}' with self.subTest("A user has no access when no access level set between user and resource"): with self.assertRaises(ResourceNotFound): get_resource_authz_parameters(user.name, resource) with self.subTest("No resource policy parameters are returned when the user tries to access a resource that " "does not use resource policies"): params = get_resource_authz_parameters(user.name, f"{self.arn_prefix}non_acl_resource/1234") self.assertFalse(params.get('ResourcePolicy')) self.assertFalse(params.get('resources')) with self.subTest("The resource policy for the access level assigned to the user is returned when a user " "is given access to a resource"): test_id = test_type.create_id(type_id) test_id.add_principals([user], access_level) params = get_resource_authz_parameters(user.name, resource) self.assertTrue(json_equal(params['ResourcePolicy'], resource_policy)) self.assertEqual(['Reader'], params['resources']) with self.subTest("No access when the user is disabled."): user.disable() with self.assertRaises(AuthorizationException): get_resource_authz_parameters(user.name, resource)
def test_put_username_roles(self): tests = [ { 'name': "*****@*****.**", 'action': 'add', 'json_request_body': { "roles": [Role.create("test_put_username_roles_role_0").name] }, 'responses': [ {'code': 200}, {'code': 304} ] }, { 'name': "*****@*****.**", 'action': 'remove', 'json_request_body': { "roles": [Role.create("test_put_username_roles_role_1").name] }, 'responses': [ {'code': 200}, {'code': 304} ] } ] for test in tests: with self.subTest(test['json_request_body']): data = json.dumps(test['json_request_body']) headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) url = furl(f'/v1/user/{test["name"]}/roles/') query_params = { 'user_id': test['name'], 'action': test['action'] } url.add(query_params=query_params) user = User.provision_user(test['name']) if test['action'] == 'remove': user.add_roles(test['json_request_body']['roles']) resp = self.app.put(url.url, headers=headers, data=data) self.assertEqual(test['responses'][0]['code'], resp.status_code) resp = self.app.put(url.url, headers=headers, data=data) self.assertEqual(test['responses'][1]['code'], resp.status_code)
def test_user_group_limit(self): groups = [Group.create(f"test_user_group_limit{i}").name for i in range(10)] name = "*****@*****.**" user = User.provision_user(name) tests = [ { 'action': 'add', 'json_request_body': { "groups": groups[:8] }, 'response': { 'code': 200 } }, { 'action': 'add', 'json_request_body': { "groups": [groups[9]] }, 'response': { 'code': 409 } } ] for test in tests: with self.subTest(test['json_request_body']): data = json.dumps(test['json_request_body']) headers = {'Content-Type': "application/json"} headers.update(get_auth_header(service_accounts['admin'])) url = furl(f'/v1/user/{name}/groups/') query_params = { 'user_id': name, 'action': test['action'] } url.add(query_params=query_params) if test['action'] == 'remove': user.add_groups(test['json_request_body']['groups']) resp = self.app.put(url.url, headers=headers, data=data) self.assertEqual(test['response']['code'], resp.status_code)
def test_group_and_role(self): """ A user inherits policies from groups and roles when the user is apart of a group and assigned a role. """ name = "*****@*****.**" user = User.provision_user(name) test_groups = [(f"group_{i}", create_test_IAMPolicy(f"GroupPolicy{i}")) for i in range(5)] [Group.create(*i) for i in test_groups] group_names, _ = zip(*test_groups) group_names = sorted(group_names) group_statements = [i[1] for i in test_groups] test_roles = [(f"role_{i}", create_test_IAMPolicy(f"RolePolicy{i}")) for i in range(5)] [Role.create(*i) for i in test_roles] role_names, _ = zip(*test_roles) role_names = sorted(role_names) role_statements = [i[1] for i in test_roles] user.add_roles(role_names) user.add_groups(group_names) user.set_policy(self.default_policy) user_role_names = [Role(object_ref=role).name for role in user.roles] user_group_names = [ Group(object_ref=group).name for group in user.groups ] self.assertListEqual(sorted(user_role_names), role_names) self.assertEqual(sorted(user_group_names), group_names + ['user_default']) authz_params = user.get_authz_params() self.assertListEqual(sorted(authz_params['roles']), sorted(['default_user'] + role_names)) self.assertListEqual(sorted(authz_params['groups']), sorted(['user_default'] + group_names)) self.assertJSONListEqual( [p['policy_document'] for p in authz_params['IAMPolicy']], [user.get_policy(), *self.default_user_policies] + group_statements + role_statements)
def test_get_user_policy(self): name = "*****@*****.**" user = User(name) with self.subTest( "new user is automatically provisioned on demand with default settings when " "lookup_policy is called for a new user."): self.assertJSONListEqual([ p['policy_document'] for p in user.get_authz_params()['IAMPolicy'] ], self.default_user_policies) with self.subTest( "error is returned when provision_user is called for an existing user" ): self.assertRaises(FusilladeHTTPException, user.provision_user, name) with self.subTest( "an existing users info is retrieved when instantiating User class for an existing user" ): user = User(name) self.assertJSONListEqual([ p['policy_document'] for p in user.get_authz_params()['IAMPolicy'] ], self.default_user_policies)
def test_get_attributes(self): name = "*****@*****.**" user = User.provision_user(name) self.assertEqual(user.get_attributes(['name'])['name'], name)
def test_user_statement(self): name = "*****@*****.**" User.provision_user(name, statement=self.default_policy) test_user = User(name) self.assertJSONEqual(test_user.get_policy(), self.default_policy)