Skip to content

Commit cbef629

Browse files
authored
feat: approve users, edit users through manage users page (#1383)
1 parent 8e8454d commit cbef629

File tree

30 files changed

+1386
-792
lines changed

30 files changed

+1386
-792
lines changed

cms/auth_backends.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from django.conf import settings
2+
from django.contrib.auth.backends import ModelBackend
3+
4+
5+
class ApprovalBackend(ModelBackend):
6+
def user_can_authenticate(self, user):
7+
can_authenticate = super().user_can_authenticate(user)
8+
if can_authenticate and settings.USERS_NEEDS_TO_BE_APPROVED and not user.is_superuser:
9+
return getattr(user, 'is_approved', False)
10+
return can_authenticate

cms/middleware.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from django.conf import settings
2+
from django.http import JsonResponse
3+
from django.shortcuts import redirect
4+
from django.urls import reverse
5+
6+
7+
class ApprovalMiddleware:
8+
def __init__(self, get_response):
9+
self.get_response = get_response
10+
11+
def __call__(self, request):
12+
if settings.USERS_NEEDS_TO_BE_APPROVED and request.user.is_authenticated and not request.user.is_superuser and not getattr(request.user, 'is_approved', False):
13+
allowed_paths = [
14+
reverse('approval_required'),
15+
reverse('account_logout'),
16+
]
17+
if request.path not in allowed_paths:
18+
if request.path.startswith('/api/'):
19+
return JsonResponse({'detail': 'User account not approved.'}, status=403)
20+
return redirect('approval_required')
21+
22+
response = self.get_response(request)
23+
return response

cms/settings.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@
128128

129129
RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = ["xxx.com", "emaildomainwhatever.com"]
130130

131+
# by default users do not need to be approved. If this is set to True, then new users
132+
# will have to be approved before they can login successfully
133+
USERS_NEEDS_TO_BE_APPROVED = False
134+
131135
# Comma separated list of domains: ["organization.com", "private.organization.com", "org2.com"]
132136
# Empty list disables.
133137
ALLOWED_DOMAINS_FOR_USER_REGISTRATION = []
@@ -501,6 +505,10 @@
501505
# Whether to allow anonymous users to list all users
502506
ALLOW_ANONYMOUS_USER_LISTING = True
503507

508+
# Who can see the members page
509+
# valid choices are all, editors, admins
510+
CAN_SEE_MEMBERS_PAGE = "all"
511+
504512
# Maximum number of media a user can upload
505513
NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100
506514

@@ -517,6 +525,9 @@
517525
# Whisper transcribe options - https://github.com/openai/whisper
518526
WHISPER_MODEL = "base"
519527

528+
# show a custom text in the sidebar footer, otherwise the default will be shown if this is empty
529+
SIDEBAR_FOOTER_TEXT = ""
530+
520531
try:
521532
# keep a local_settings.py file for local overrides
522533
from .local_settings import * # noqa
@@ -558,3 +569,12 @@
558569
if GLOBAL_LOGIN_REQUIRED:
559570
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
560571
MIDDLEWARE.insert(auth_index + 1, "django.contrib.auth.middleware.LoginRequiredMiddleware")
572+
573+
574+
if USERS_NEEDS_TO_BE_APPROVED:
575+
AUTHENTICATION_BACKENDS = (
576+
'cms.auth_backends.ApprovalBackend',
577+
'allauth.account.auth_backends.AuthenticationBackend',
578+
)
579+
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
580+
MIDDLEWARE.insert(auth_index + 1, "cms.middleware.ApprovalMiddleware")

cms/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = "6.5.2"
1+
VERSION = "6.6.0"

docs/admins_docs.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,20 @@ ALLOW_ANONYMOUS_USER_LISTING = False
519519
When set to False, only logged-in users will be able to access the user listing API endpoint.
520520

521521

522+
### 5.27 Control who can see the members page
523+
524+
By default `CAN_SEE_MEMBERS_PAGE = "all"` means that all registered users can see the members page. Other valid options are:
525+
526+
- **editors**, only MediaCMS editors can view the page
527+
- **admins**, only MediaCMS admins can view the page
528+
529+
530+
### 5.28 Require user approval on registration
531+
532+
By default, users do not require approval, so they can login immediately after registration (if registration is open). However, if the parameter `USERS_NEEDS_TO_BE_APPROVED` is set to `True`, they will first have to have their accounts approved by an administrator before they can successfully sign in.
533+
Administrators can approve users through the following ways: 1. through Django administration, 2. through the users management page, 3. through editing the profile page directly. In all cases, set 'Is approved' to True.
534+
535+
522536
## 6. Manage pages
523537
to be written
524538

files/context_processors.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,22 @@ def stuff(request):
2626
ret["UPLOAD_MAX_SIZE"] = settings.UPLOAD_MAX_SIZE
2727
ret["UPLOAD_MAX_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER
2828
ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE
29+
ret["SIDEBAR_FOOTER_TEXT"] = settings.SIDEBAR_FOOTER_TEXT
2930
ret["POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY
3031
ret["IS_MEDIACMS_ADMIN"] = request.user.is_superuser
3132
ret["IS_MEDIACMS_EDITOR"] = is_mediacms_editor(request.user)
3233
ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user)
34+
ret["USERS_NEEDS_TO_BE_APPROVED"] = settings.USERS_NEEDS_TO_BE_APPROVED
35+
36+
can_see_members_page = False
37+
if request.user.is_authenticated:
38+
if settings.CAN_SEE_MEMBERS_PAGE == "all":
39+
can_see_members_page = True
40+
elif settings.CAN_SEE_MEMBERS_PAGE == "editors" and is_mediacms_editor(request.user):
41+
can_see_members_page = True
42+
elif settings.CAN_SEE_MEMBERS_PAGE == "admins" and request.user.is_superuser:
43+
can_see_members_page = True
44+
ret["CAN_SEE_MEMBERS_PAGE"] = can_see_members_page
3345
ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS
3446
ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
3547
ret["VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE

files/management_views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from django.conf import settings
2+
from django.db.models import Q
13
from drf_yasg import openapi as openapi
24
from drf_yasg.utils import swagger_auto_schema
35
from rest_framework import status
@@ -219,6 +221,13 @@ def get(self, request, format=None):
219221
elif role == "editor":
220222
qs = qs.filter(is_editor=True)
221223

224+
if settings.USERS_NEEDS_TO_BE_APPROVED:
225+
is_approved = request.GET.get("is_approved")
226+
if is_approved == "true":
227+
qs = qs.filter(is_approved=True)
228+
elif is_approved == "false":
229+
qs = qs.filter(Q(is_approved=False) | Q(is_approved__isnull=True))
230+
222231
users = qs.order_by(f"{ordering}{sort_by}")
223232

224233
paginator = pagination_class()

files/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@
110110
re_path(r"^manage/users$", views.manage_users, name="manage_users"),
111111
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
112112

113+
if settings.USERS_NEEDS_TO_BE_APPROVED:
114+
urlpatterns.append(re_path(r"^approval_required", views.approval_required, name="approval_required"))
115+
113116
if hasattr(settings, "USE_SAML") and settings.USE_SAML:
114117
urlpatterns.append(re_path(r"^saml/metadata", views.saml_metadata, name="saml-metadata"))
115118

files/views/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Import all views for backward compatibility
2+
23
from .auth import custom_login_view, saml_metadata # noqa: F401
34
from .categories import CategoryList, TagList # noqa: F401
45
from .comments import CommentDetail, CommentList # noqa: F401
@@ -10,6 +11,7 @@
1011
from .media import MediaSearch # noqa: F401
1112
from .pages import about # noqa: F401
1213
from .pages import add_subtitle # noqa: F401
14+
from .pages import approval_required # noqa: F401
1315
from .pages import categories # noqa: F401
1416
from .pages import contact # noqa: F401
1517
from .pages import edit_chapters # noqa: F401

files/views/pages.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ def about(request):
5454
return render(request, "cms/about.html", context)
5555

5656

57+
def approval_required(request):
58+
"""User needs approval view"""
59+
return render(request, "cms/user_needs_approval.html", {})
60+
61+
5762
def setlanguage(request):
5863
"""Set Language view"""
5964

@@ -517,6 +522,12 @@ def manage_comments(request):
517522
def members(request):
518523
"""List members view"""
519524

525+
if settings.CAN_SEE_MEMBERS_PAGE == "editors" and not is_mediacms_editor(request.user):
526+
return HttpResponseRedirect("/")
527+
528+
if settings.CAN_SEE_MEMBERS_PAGE == "admins" and not request.user.is_superuser:
529+
return HttpResponseRedirect("/")
530+
520531
context = {}
521532
return render(request, "cms/members.html", context)
522533

0 commit comments

Comments
 (0)