def get_queryset(self): """ Builds a queryset of relevant users with permissions for this website, and annotates them by group name/role (owner, administrator, editor, or global administrator) """ website = self.website website_group_names = list( get_groups_with_perms(website).values_list( "name", flat=True)) + [constants.GLOBAL_ADMIN] owner_user_id = website.owner.id if website.owner else None return (User.objects.filter( Q(id=owner_user_id) | Q(groups__name__in=website_group_names)).annotate(role=Case( When(id=owner_user_id, then=Value(constants.ROLE_OWNER)), default=Group.objects.filter( user__id=OuterRef("id"), name__in=website_group_names).annotate(role_name=Case( *([ When( name=permissions_group_name_for_role( role, website), then=Value(role), ) for role in constants.ROLE_GROUP_MAPPING ]), output_field=CharField(), )).values("role_name")[:1], output_field=CharField(), )).order_by("name", "id").distinct())
def setup_website_groups_permissions(website): """ Create groups and assign permissions for a website Args: website (Website): The website that the permissions are for Returns: list (int, int, bool): # groups created, # groups updated, owner updated """ groups_created = 0 groups_updated = 0 owner_updated = False admin_group, admin_created = Group.objects.get_or_create( name=permissions_group_name_for_role(constants.ROLE_ADMINISTRATOR, website)) if admin_created: groups_created += 1 editor_group, editor_created = Group.objects.get_or_create( name=permissions_group_name_for_role(constants.ROLE_EDITOR, website)) if editor_created: groups_created += 1 if (assign_website_permissions( admin_group, constants.PERMISSIONS_ADMIN, website=website) and not admin_created): groups_updated += 1 if (assign_website_permissions( editor_group, constants.PERMISSIONS_EDITOR, website=website) and not editor_created): groups_updated += 1 if website.owner: owner_updated = assign_website_permissions(website.owner, constants.PERMISSIONS_ADMIN, website=website) return groups_created, groups_updated, owner_updated
def create(self, validated_data): """Creating a contributor adds the user to the group corresponding to the specified role""" website = self.website role = validated_data.get("role") user = User.objects.get(email=validated_data.get("email")) group_name = permissions_group_name_for_role(role, website) if user in get_users_with_perms(website) or user == website.owner: raise ValidationError( {"email": ["User is already a collaborator for this site"]}) user.groups.add(Group.objects.get(name=group_name)) # user.role normally gets set by the query in the view, but we need to manually set/update it here user.role = role return user
def is_site_admin(user, website): """ Determine if the user is effectively an admin for a site Args: user (users.models.User): The user to check website (Website): The website to check Returns: bool: True if user is an admin for the site """ group_names = set(user.groups.values_list("name", flat=True)) return (is_global_admin(user, group_names=group_names) or website.owner == user or permissions_group_name_for_role( constants.ROLE_ADMINISTRATOR, website) in group_names)
def update(self, instance, validated_data): """ Change a collaborator's permission group for the website """ website = self.website user = instance role = validated_data.get("role") group_name = permissions_group_name_for_role(role, website) # User should only belong to one group per website for group in get_groups_with_perms(website): if group_name and group.name == group_name: user.groups.add(group) else: user.groups.remove(group) # user.role normally gets set by the query in the view, but we need to manually set/update it here user.role = role return user
def editor_group(self): """ Get the editor group """ return Group.objects.filter( name=permissions_group_name_for_role(constants.ROLE_EDITOR, self) ).first()
def admin_group(self): """ Get the admin group """ return Group.objects.filter( name=permissions_group_name_for_role(constants.ROLE_ADMINISTRATOR, self) ).first()
def test_permissions_group_for_role_invalid(role): """permissions_group_for_role should raise a ValueError for an invalid role""" website = WebsiteFactory.build() with pytest.raises(ValueError) as exc: permissions_group_name_for_role(role, website) assert exc.value.args == (f"Invalid role for a website group: {role}", )
def test_permissions_group_name_for_global_admin(): """permissions_group_for_role should return the correct group name for global admins""" website = WebsiteFactory.build() assert (permissions_group_name_for_role(constants.ROLE_GLOBAL, website) == constants.GLOBAL_ADMIN)
def test_permissions_group_name_for_role(role, group_prefix): """permissions_group_for_role should return the correct group name for a website and role""" website = WebsiteFactory.build() assert (permissions_group_name_for_role( role, website) == f"{group_prefix}{website.uuid.hex}")