Allow configuring guest user profile (#809)

This commit is contained in:
Sascha Ißbrücker 2024-08-31 20:25:43 +02:00 committed by GitHub
parent 79bf4b38c6
commit aad62f61c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 127 additions and 8 deletions

View file

@ -1,13 +1,17 @@
from django.conf import settings from django.conf import settings
from django.contrib.auth.middleware import RemoteUserMiddleware from django.contrib.auth.middleware import RemoteUserMiddleware
from bookmarks.models import UserProfile from bookmarks.models import UserProfile, GlobalSettings
class CustomRemoteUserMiddleware(RemoteUserMiddleware): class CustomRemoteUserMiddleware(RemoteUserMiddleware):
header = settings.LD_AUTH_PROXY_USERNAME_HEADER header = settings.LD_AUTH_PROXY_USERNAME_HEADER
standard_profile = UserProfile()
standard_profile.enable_favicons = True
class UserProfileMiddleware: class UserProfileMiddleware:
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response
@ -16,8 +20,16 @@ class UserProfileMiddleware:
if request.user.is_authenticated: if request.user.is_authenticated:
request.user_profile = request.user.profile request.user_profile = request.user.profile
else: else:
request.user_profile = UserProfile() # check if a custom profile for guests exists, otherwise use standard profile
request.user_profile.enable_favicons = True guest_profile = None
try:
global_settings = GlobalSettings.get()
if global_settings.guest_profile_user:
guest_profile = global_settings.guest_profile_user.profile
except:
pass
request.user_profile = guest_profile or standard_profile
response = self.get_response(request) response = self.get_response(request)

View file

@ -0,0 +1,26 @@
# Generated by Django 5.0.8 on 2024-08-31 17:54
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookmarks", "0037_globalsettings"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name="globalsettings",
name="guest_profile_user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -508,6 +508,9 @@ class GlobalSettings(models.Model):
blank=False, blank=False,
default=LANDING_PAGE_LOGIN, default=LANDING_PAGE_LOGIN,
) )
guest_profile_user = models.ForeignKey(
get_user_model(), on_delete=models.SET_NULL, null=True, blank=True
)
@classmethod @classmethod
def get(cls): def get(cls):
@ -526,6 +529,8 @@ class GlobalSettings(models.Model):
class GlobalSettingsForm(forms.ModelForm): class GlobalSettingsForm(forms.ModelForm):
class Meta: class Meta:
model = GlobalSettings model = GlobalSettings
fields = [ fields = ["landing_page", "guest_profile_user"]
"landing_page",
] def __init__(self, *args, **kwargs):
super(GlobalSettingsForm, self).__init__(*args, **kwargs)
self.fields["guest_profile_user"].empty_label = "Standard profile"

View file

@ -249,7 +249,17 @@ reddit.com/r/Music music reddit</pre>
<label for="{{ global_settings_form.landing_page.id_for_label }}" class="form-label">Landing page</label> <label for="{{ global_settings_form.landing_page.id_for_label }}" class="form-label">Landing page</label>
{{ global_settings_form.landing_page|add_class:"form-select width-25 width-sm-100" }} {{ global_settings_form.landing_page|add_class:"form-select width-25 width-sm-100" }}
<div class="form-input-hint"> <div class="form-input-hint">
The page that unauthorized users are redirected to when accessing the root URL. The page that unauthenticated users are redirected to when accessing the root URL.
</div>
</div>
<div class="form-group">
<label for="{{ global_settings_form.guest_profile_user.id_for_label }}" class="form-label">Guest user
profile</label>
{{ global_settings_form.guest_profile_user|add_class:"form-select width-25 width-sm-100" }}
<div class="form-input-hint">
The user profile to use for users that are not logged in. This will affect how publicly shared bookmarks
are displayed regarding theme, bookmark list settings, etc. You can either use your own profile or create
a dedicated user for this purpose. By default, a standard profile with fixed settings is used.
</div> </div>
</div> </div>

View file

@ -5,7 +5,7 @@ from bookmarks.models import GlobalSettings
from bookmarks.tests.helpers import BookmarkFactoryMixin from bookmarks.tests.helpers import BookmarkFactoryMixin
class AnonymousViewTestCase(TestCase, BookmarkFactoryMixin): class RootViewTestCase(TestCase, BookmarkFactoryMixin):
def test_unauthenticated_user_redirect_to_login_by_default(self): def test_unauthenticated_user_redirect_to_login_by_default(self):
response = self.client.get(reverse("bookmarks:root")) response = self.client.get(reverse("bookmarks:root"))
self.assertRedirects(response, reverse("login")) self.assertRedirects(response, reverse("login"))

View file

@ -469,10 +469,13 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
def test_update_global_settings(self): def test_update_global_settings(self):
superuser = self.setup_superuser() superuser = self.setup_superuser()
self.client.force_login(superuser) self.client.force_login(superuser)
selectable_user = self.setup_user()
# Update global settings
form_data = { form_data = {
"update_global_settings": "", "update_global_settings": "",
"landing_page": GlobalSettings.LANDING_PAGE_SHARED_BOOKMARKS, "landing_page": GlobalSettings.LANDING_PAGE_SHARED_BOOKMARKS,
"guest_profile_user": selectable_user.id,
} }
response = self.client.post(reverse("bookmarks:settings.general"), form_data) response = self.client.post(reverse("bookmarks:settings.general"), form_data)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -480,6 +483,22 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
global_settings = GlobalSettings.get() global_settings = GlobalSettings.get()
self.assertEqual(global_settings.landing_page, form_data["landing_page"]) self.assertEqual(global_settings.landing_page, form_data["landing_page"])
self.assertEqual(global_settings.guest_profile_user, selectable_user)
# Revert settings
form_data = {
"update_global_settings": "",
"landing_page": GlobalSettings.LANDING_PAGE_LOGIN,
"guest_profile_user": "",
}
response = self.client.post(reverse("bookmarks:settings.general"), form_data)
self.assertEqual(response.status_code, 200)
self.assertSuccessMessage(response.content.decode(), "Global settings updated")
global_settings = GlobalSettings.get()
global_settings.refresh_from_db()
self.assertEqual(global_settings.landing_page, form_data["landing_page"])
self.assertIsNone(global_settings.guest_profile_user)
def test_update_global_settings_should_not_be_called_without_respective_form_action( def test_update_global_settings_should_not_be_called_without_respective_form_action(
self, self,

View file

@ -0,0 +1,47 @@
from django.test import TestCase
from django.urls import reverse
from bookmarks.models import UserProfile, GlobalSettings
from bookmarks.tests.helpers import BookmarkFactoryMixin
from bookmarks.middlewares import standard_profile
class UserProfileMiddlewareTestCase(TestCase, BookmarkFactoryMixin):
def test_unauthenticated_user_should_use_standard_profile_by_default(self):
response = self.client.get(reverse("login"))
self.assertEqual(standard_profile, response.wsgi_request.user_profile)
def test_unauthenticated_user_should_use_custom_configured_profile(self):
guest_user = self.setup_user()
guest_user_profile = guest_user.profile
guest_user_profile.theme = UserProfile.THEME_DARK
guest_user_profile.save()
global_settings = GlobalSettings.get()
global_settings.guest_profile_user = guest_user
global_settings.save()
response = self.client.get(reverse("login"))
self.assertEqual(guest_user_profile, response.wsgi_request.user_profile)
def test_authenticated_user_should_use_own_profile(self):
guest_user = self.setup_user()
guest_user_profile = guest_user.profile
guest_user_profile.theme = UserProfile.THEME_DARK
guest_user_profile.save()
global_settings = GlobalSettings.get()
global_settings.guest_profile_user = guest_user
global_settings.save()
user = self.get_or_create_test_user()
user_profile = user.profile
user_profile.theme = UserProfile.THEME_LIGHT
user_profile.save()
self.client.force_login(user)
response = self.client.get(reverse("login"), follow=True)
self.assertEqual(user_profile, response.wsgi_request.user_profile)