def _post_save(self, instance, created, *args, **kwargs): Role_ = utils.get_current_apps().get_model('main', 'Role') ContentType_ = utils.get_current_apps().get_model('contenttypes', 'ContentType') ct_id = ContentType_.objects.get_for_model(instance).id Model = utils.get_current_apps().get_model('main', instance.__class__.__name__) latest_instance = Model.objects.get(pk=instance.pk) with batch_role_ancestor_rebuilding(): # Create any missing role objects missing_roles = [] for implicit_role_field in getattr(latest_instance.__class__, '__implicit_role_fields'): cur_role = getattr(latest_instance, implicit_role_field.name, None) if cur_role is None: missing_roles.append(Role_(role_field=implicit_role_field.name, content_type_id=ct_id, object_id=latest_instance.id)) if len(missing_roles) > 0: Role_.objects.bulk_create(missing_roles) updates = {} role_ids = [] for role in Role_.objects.filter(content_type_id=ct_id, object_id=latest_instance.id): setattr(latest_instance, role.role_field, role) updates[role.role_field] = role.id role_ids.append(role.id) type(latest_instance).objects.filter(pk=latest_instance.pk).update(**updates) Role.rebuild_role_ancestor_list(role_ids, []) update_role_parentage_for_instance(latest_instance) instance.refresh_from_db()
def test_roles_visibility(get, organization, project, admin, alice, bob): Role.singleton('system_auditor').members.add(alice) assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=admin).data['count'] == 1 assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=alice).data['count'] == 1 assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=bob).data['count'] == 0 organization.auditor_role.members.add(bob) assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=bob).data['count'] == 1
def user_is_system_auditor(user, tf): if user.id: if tf: Role.singleton('system_auditor').members.add(user) user._is_system_auditor = True else: Role.singleton('system_auditor').members.remove(user) user._is_system_auditor = False
def _post_delete(self, instance, *args, **kwargs): role_ids = [] for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): role_ids.append(getattr(instance, implicit_role_field.name + '_id')) Role_ = utils.get_current_apps().get_model('main', 'Role') child_ids = [x for x in Role_.parents.through.objects.filter(to_role_id__in=role_ids).distinct().values_list('from_role_id', flat=True)] Role_.objects.filter(id__in=role_ids).delete() Role.rebuild_role_ancestor_list([], child_ids)
def test_metrics_permissions(get, admin, org_admin, alice, bob, organization): assert get(get_metrics_view_db_only(), user=admin).status_code == 200 assert get(get_metrics_view_db_only(), user=org_admin).status_code == 403 assert get(get_metrics_view_db_only(), user=alice).status_code == 403 assert get(get_metrics_view_db_only(), user=bob).status_code == 403 organization.auditor_role.members.add(bob) assert get(get_metrics_view_db_only(), user=bob).status_code == 403 Role.singleton('system_auditor').members.add(bob) bob.is_system_auditor = True assert get(get_metrics_view_db_only(), user=bob).status_code == 200
def test_roles_filter_visibility(get, organization, project, admin, alice, bob): Role.singleton('system_auditor').members.add(alice) project.update_role.members.add(admin) assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=admin).data['count'] == 1 assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=alice).data['count'] == 1 assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=bob).data['count'] == 0 organization.auditor_role.members.add(bob) assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=bob).data['count'] == 1 organization.auditor_role.members.remove(bob) project.use_role.members.add(bob) # sibling role should still grant visibility assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=bob).data['count'] == 1
def user_is_system_auditor(user, tf): if user.id: if tf: role = Role.singleton('system_auditor') # must check if member to not duplicate activity stream if user not in role.members.all(): role.members.add(user) user._is_system_auditor = True else: role = Role.singleton('system_auditor') if user in role.members.all(): role.members.remove(user) user._is_system_auditor = False
def rebuild_role_hierarchy(apps, schema_editor): logger.info('Computing role roots..') start = time() roots = Role.objects \ .all() \ .values_list('id', flat=True) stop = time() logger.info('Found %d roots in %f seconds, rebuilding ancestry map' % (len(roots), stop - start)) start = time() Role.rebuild_role_ancestor_list(roots, []) stop = time() logger.info('Rebuild completed in %f seconds' % (stop - start)) logger.info('Done.')
def apply_fake_roles(obj): ''' Creates an un-saved role for all the implicit role fields on an object ''' for fd in obj._meta.fields: if not isinstance(fd, ImplicitRoleField): continue r = Role(role_field=fd.name) setattr(obj, fd.name, r) with mock.patch( 'django.contrib.contenttypes.fields.GenericForeignKey.get_content_type' ) as mck_ct: mck_ct.return_value = ContentType(model=obj._meta.model_name) r.content_object = obj
def test_get_roles_list_user(organization, inventory, team, get, user): 'Users can see all roles they have access to, but not all roles' this_user = user('user-test_get_roles_list_user') organization.member_role.members.add(this_user) custom_role = Role.objects.create( role_field='custom_role-test_get_roles_list_user') organization.member_role.children.add(custom_role) url = reverse('api:role_list') response = get(url, this_user) assert response.status_code == 200 roles = response.data assert roles['count'] > 0 assert roles['count'] == len( roles['results']) # just to make sure the tests below are valid role_hash = {} for r in roles['results']: role_hash[r['id']] = r assert Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id in role_hash assert organization.admin_role.id in role_hash assert organization.member_role.id in role_hash assert this_user.admin_role.id in role_hash assert custom_role.id in role_hash assert inventory.admin_role.id not in role_hash assert team.member_role.id not in role_hash
def rebuild_role_hierarchy(apps, schema_editor): """ This should be called in any migration when ownerships are changed. Ex. I remove a user from the admin_role of a credential. Ancestors are cached from parents for performance, this re-computes ancestors. """ logger.info('Computing role roots..') start = time() roots = Role.objects.all().values_list('id', flat=True) stop = time() logger.info('Found %d roots in %f seconds, rebuilding ancestry map' % (len(roots), stop - start)) start = time() Role.rebuild_role_ancestor_list(roots, []) stop = time() logger.info('Rebuild ancestors completed in %f seconds' % (stop - start)) logger.info('Done.')
def user_is_system_auditor(user, tf): if not user.id: # If the user doesn't have a primary key yet (i.e., this is the *first* # time they've logged in, and we've just created the new User in this # request), we need one to set up the system auditor role user.save() if tf: role = Role.singleton('system_auditor') # must check if member to not duplicate activity stream if user not in role.members.all(): role.members.add(user) user._is_system_auditor = True else: role = Role.singleton('system_auditor') if user in role.members.all(): role.members.remove(user) user._is_system_auditor = False
def _post_save(self, instance, created, *args, **kwargs): Role_ = utils.get_current_apps().get_model('main', 'Role') ContentType_ = utils.get_current_apps().get_model( 'contenttypes', 'ContentType') ct_id = ContentType_.objects.get_for_model(instance).id with batch_role_ancestor_rebuilding(): # Create any missing role objects missing_roles = [] for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): cur_role = getattr(instance, implicit_role_field.name, None) if cur_role is None: missing_roles.append( Role_(role_field=implicit_role_field.name, content_type_id=ct_id, object_id=instance.id)) if len(missing_roles) > 0: Role_.objects.bulk_create(missing_roles) updates = {} role_ids = [] for role in Role_.objects.filter(content_type_id=ct_id, object_id=instance.id): setattr(instance, role.role_field, role) updates[role.role_field] = role.id role_ids.append(role.id) type(instance).objects.filter(pk=instance.pk).update(**updates) Role.rebuild_role_ancestor_list(role_ids, []) # Update parentage if necessary for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): cur_role = getattr(instance, implicit_role_field.name) original_parents = set(json.loads(cur_role.implicit_parents)) new_parents = implicit_role_field._resolve_parent_roles( instance) cur_role.parents.remove(*list(original_parents - new_parents)) cur_role.parents.add(*list(new_parents - original_parents)) new_parents_list = list(new_parents) new_parents_list.sort() new_parents_json = json.dumps(new_parents_list) if cur_role.implicit_parents != new_parents_json: cur_role.implicit_parents = new_parents_json cur_role.save()
def test_user_view_other_user_roles(organization, inventory, team, get, alice, bob): 'Users can see roles for other users, but only the roles that that user has access to see as well' organization.member_role.members.add(alice) organization.admin_role.members.add(bob) organization.member_role.members.add(bob) custom_role = Role.objects.create( role_field='custom_role-test_user_view_admin_roles_list') organization.member_role.children.add(custom_role) team.member_role.members.add(bob) # alice and bob are in the same org and can see some child role of that org. # Bob is an org admin, alice can see this. # Bob is in a team that alice is not, alice cannot see that bob is a member of that team. url = reverse('api:user_roles_list', kwargs={'pk': bob.id}) response = get(url, alice) assert response.status_code == 200 roles = response.data assert roles['count'] > 0 assert roles['count'] == len( roles['results']) # just to make sure the tests below are valid role_hash = {} for r in roles['results']: role_hash[r['id']] = r['name'] assert organization.admin_role.id in role_hash assert custom_role.id not in role_hash # doesn't show up in the user roles list, not an explicit grant assert Role.singleton( ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id not in role_hash assert inventory.admin_role.id not in role_hash assert team.member_role.id not in role_hash # alice can't see this # again but this time alice is part of the team, and should be able to see the team role team.member_role.members.add(alice) response = get(url, alice) assert response.status_code == 200 roles = response.data assert roles['count'] > 0 assert roles['count'] == len( roles['results']) # just to make sure the tests below are valid role_hash = {} for r in roles['results']: role_hash[r['id']] = r['name'] assert team.member_role.id in role_hash # Alice can now see this
def system_auditor(user): u = user('an-auditor', False) Role.singleton('system_auditor').members.add(u) return u
def rebuild_role_parentage(apps, schema_editor, models=None): """ This should be called in any migration when any parent_role entry is modified so that the cached parent fields will be updated. Ex: foo_role = ImplicitRoleField( parent_role=['bar_role'] # change to parent_role=['admin_role'] ) This is like rebuild_role_hierarchy, but that method updates ancestors, whereas this method updates parents. """ start = time() seen_models = set() model_ct = 0 noop_ct = 0 ContentType = apps.get_model('contenttypes', "ContentType") additions = set() removals = set() role_qs = Role.objects if models: # update_role_parentage_for_instance is expensive # if the models have been downselected, ignore those which are not in the list ct_ids = list( ContentType.objects.filter( model__in=[name.lower() for name in models]).values_list('id', flat=True)) role_qs = role_qs.filter(content_type__in=ct_ids) for role in role_qs.iterator(): if not role.object_id: continue model_tuple = (role.content_type_id, role.object_id) if model_tuple in seen_models: continue seen_models.add(model_tuple) # The GenericForeignKey does not work right in migrations # with the usage as role.content_object # so we do the lookup ourselves with current migration models ct = role.content_type app = ct.app_label ct_model = apps.get_model(app, ct.model) content_object = ct_model.objects.get(pk=role.object_id) parents_added, parents_removed = update_role_parentage_for_instance( content_object) additions.update(parents_added) removals.update(parents_removed) if parents_added: model_ct += 1 logger.debug('Added to parents of roles {} of {}'.format( parents_added, content_object)) if parents_removed: model_ct += 1 logger.debug('Removed from parents of roles {} of {}'.format( parents_removed, content_object)) else: noop_ct += 1 logger.debug('No changes to role parents for {} resources'.format(noop_ct)) logger.debug('Added parents to {} roles'.format(len(additions))) logger.debug('Removed parents from {} roles'.format(len(removals))) if model_ct: logger.info( 'Updated implicit parents of {} resources'.format(model_ct)) logger.info('Rebuild parentage completed in %f seconds' % (time() - start)) # this is ran because the ordinary signals for # Role.parents.add and Role.parents.remove not called in migration Role.rebuild_role_ancestor_list(list(additions), list(removals))
def system_auditor(): return Role(role_field=ROLE_SINGLETON_SYSTEM_AUDITOR, singleton_name=ROLE_SINGLETON_SYSTEM_AUDITOR)
def system_administrator(): return Role(role_field=ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, singleton_name=ROLE_SINGLETON_SYSTEM_ADMINISTRATOR)