feat: whitelist for user visibility

This commit is contained in:
imperosol
2026-03-14 16:43:00 +01:00
parent d374ea9651
commit 1d672a5fc2
8 changed files with 86 additions and 26 deletions

View File

@@ -131,7 +131,7 @@ class UserQuerySet(models.QuerySet):
if user.has_perm("core.view_hidden_user"):
return self
if user.has_perm("core.view_user"):
return self.filter(is_viewable=True)
return self.filter(Q(is_viewable=True) | Q(whitelisted_users=user))
if user.is_anonymous:
return self.none()
return self.filter(id=user.id)
@@ -279,6 +279,15 @@ class User(AbstractUser):
),
default=True,
)
whitelisted_users = models.ManyToManyField(
"User",
related_name="visible_by_whitelist",
verbose_name=_("whitelisted users"),
help_text=_(
"Even if this profile is hidden, "
"the users in this list will still be able to see it."
),
)
godfathers = models.ManyToManyField("User", related_name="godchildren", blank=True)
objects = CustomUserManager()
@@ -518,7 +527,7 @@ class User(AbstractUser):
self.username = user_name
return user_name
def is_owner(self, obj):
def is_owner(self, obj: models.Model):
"""Determine if the object is owned by the user."""
if hasattr(obj, "is_owned_by") and obj.is_owned_by(self):
return True
@@ -526,7 +535,7 @@ class User(AbstractUser):
return True
return self.is_root
def can_edit(self, obj):
def can_edit(self, obj: models.Model):
"""Determine if the object can be edited by the user."""
if hasattr(obj, "can_be_edited_by") and obj.can_be_edited_by(self):
return True
@@ -540,11 +549,9 @@ class User(AbstractUser):
pks = list(obj.edit_groups.values_list("id", flat=True))
if any(self.is_in_group(pk=pk) for pk in pks):
return True
if isinstance(obj, User) and obj == self:
return True
return self.is_owner(obj)
def can_view(self, obj):
def can_view(self, obj: models.Model):
"""Determine if the object can be viewed by the user."""
if hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self):
return True
@@ -563,14 +570,35 @@ class User(AbstractUser):
return True
return self.can_edit(obj)
def can_be_edited_by(self, user):
return user.is_root or user.is_board_member
def can_be_edited_by(self, user: User):
return user == self or user.is_root or user.is_board_member
def can_be_viewed_by(self, user: User) -> bool:
"""Check if the given user can be viewed by this user.
Given users A and B. A can be viewed by B if :
- A and B are the same user
- or B has the permission to view hidden users
- or B can view users in general and A didn't hide its profile
- or B is in A's whitelist.
"""
def is_in_whitelist(u: User):
if (
hasattr(self, "_prefetched_objects_cache")
and "whitelisted_users" in self._prefetched_objects_cache
):
return u in self.whitelisted_users.all()
return self.whitelisted_users.contains(u)
return (
user.id == self.id
or user.has_perm("core.view_hidden_user")
or (user.has_perm("core.view_user") and self.is_viewable)
or (
user.has_perm("core.view_user")
and (self.is_viewable or is_in_whitelist(user))
)
)
def get_mini_item(self):
@@ -750,7 +778,11 @@ class Preferences(models.Model):
User, related_name="_preferences", on_delete=models.CASCADE
)
receive_weekmail = models.BooleanField(_("receive the Weekmail"), default=False)
show_my_stats = models.BooleanField(_("show your stats to others"), default=False)
show_my_stats = models.BooleanField(
_("show your stats to others"),
help_text=_("Allow subscribers to access your AE account stats."),
default=False,
)
notify_on_click = models.BooleanField(
_("get a notification for every click"), default=False
)