1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-25 02:00:06 +00:00

shib second uid (#6175)

* second uid for shibboleth

* update test
This commit is contained in:
lian 2024-06-12 17:24:24 +08:00 committed by GitHub
parent ac1c00442e
commit 92445b16ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 44 deletions

View File

@ -1,17 +1,12 @@
# Copyright (c) 2012-2016 Seafile Ltd. # Copyright (c) 2012-2016 Seafile Ltd.
import datetime
import hashlib import hashlib
import urllib.request, urllib.parse, urllib.error
import logging import logging
from registration.signals import user_deleted
# import auth
from django.core.exceptions import ImproperlyConfigured
from django.db import models from django.db import models
from django.db.models.manager import EmptyManager
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.dispatch import receiver
from django.utils.encoding import smart_str
from django.db.models.manager import EmptyManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
UNUSABLE_PASSWORD = '!' # This will never be a valid hash UNUSABLE_PASSWORD = '!' # This will never be a valid hash
@ -130,15 +125,26 @@ class AnonymousUser(object):
class SocialAuthUserManager(models.Manager): class SocialAuthUserManager(models.Manager):
def add(self, username, provider, uid, extra_data=''): def add(self, username, provider, uid, extra_data=''):
try: try:
social_auth_user = self.model(username=username, provider=provider, uid=uid, extra_data=extra_data) social_auth_user = self.model(username=username, provider=provider,
uid=uid, extra_data=extra_data)
social_auth_user.save() social_auth_user.save()
return social_auth_user return social_auth_user
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return None return None
def add_if_not_exists(self, username, provider, uid, extra_data=''):
social_auth_user = self.get_by_provider_and_uid(provider, uid)
if not social_auth_user:
social_auth_user = self.add(username, provider,
uid, extra_data=extra_data)
return social_auth_user
def get_by_provider_and_uid(self, provider, uid): def get_by_provider_and_uid(self, provider, uid):
try: try:
social_auth_user = self.get(provider=provider, uid=uid) social_auth_user = self.get(provider=provider, uid=uid)
@ -186,11 +192,7 @@ class ExternalDepartment(models.Model):
db_table = 'external_department' db_table = 'external_department'
# # handle signals # handle signals
from django.dispatch import receiver
from registration.signals import user_deleted
@receiver(user_deleted) @receiver(user_deleted)
def user_deleted_cb(sender, **kwargs): def user_deleted_cb(sender, **kwargs):
username = kwargs['username'] username = kwargs['username']

View File

@ -48,7 +48,7 @@ class ShibbolethRemoteUserMiddlewareTest(BaseTestCase):
self.request.META = {} self.request.META = {}
self.request.META['Shibboleth-eppn'] = 'sampledeveloper@school.edu' self.request.META['Shibboleth-eppn'] = 'sampledeveloper@school.edu'
self.request.META['REMOTE_USER'] = 'sampledeveloper@school.edu' self.request.META['HTTP_REMOTE_USER'] = 'sampledeveloper@school.edu'
self.request.META['givenname'] = 'test_gname' self.request.META['givenname'] = 'test_gname'
self.request.META['surname'] = 'test_sname' self.request.META['surname'] = 'test_sname'
self.request.META['Shibboleth-displayName'] = 'Sample Developer' self.request.META['Shibboleth-displayName'] = 'Sample Developer'
@ -68,6 +68,12 @@ class ShibbolethRemoteUserMiddlewareTest(BaseTestCase):
def test_can_process(self): def test_can_process(self):
assert len(Profile.objects.all()) == 0 assert len(Profile.objects.all()) == 0
# logout first
from seahub.auth.models import AnonymousUser
self.request.session.flush()
self.request.user = AnonymousUser()
# then login user via thibboleth
self.middleware.process_request(self.request) self.middleware.process_request(self.request)
shib_user = SocialAuthUser.objects.get_by_provider_and_uid( shib_user = SocialAuthUser.objects.get_by_provider_and_uid(
SHIBBOLETH_PROVIDER_IDENTIFIER, 'sampledeveloper@school.edu') SHIBBOLETH_PROVIDER_IDENTIFIER, 'sampledeveloper@school.edu')
@ -95,6 +101,12 @@ class ShibbolethRemoteUserMiddlewareTest(BaseTestCase):
def test_can_process_user_role(self): def test_can_process_user_role(self):
assert len(Profile.objects.all()) == 0 assert len(Profile.objects.all()) == 0
# logout first
from seahub.auth.models import AnonymousUser
self.request.session.flush()
self.request.user = AnonymousUser()
# then login user via thibboleth
self.middleware.process_request(self.request) self.middleware.process_request(self.request)
shib_user = SocialAuthUser.objects.get_by_provider_and_uid( shib_user = SocialAuthUser.objects.get_by_provider_and_uid(
SHIBBOLETH_PROVIDER_IDENTIFIER, 'sampledeveloper@school.edu') SHIBBOLETH_PROVIDER_IDENTIFIER, 'sampledeveloper@school.edu')
@ -191,4 +203,3 @@ class ShibbolethRemoteUserMiddlewareTest(BaseTestCase):
assert obj._get_role_by_affiliation('student1@school.edu') == 'student' assert obj._get_role_by_affiliation('student1@school.edu') == 'student'
assert obj._get_role_by_affiliation('a@x.edu') == 'aaa' assert obj._get_role_by_affiliation('a@x.edu') == 'aaa'
assert obj._get_role_by_affiliation('a@x.com') == 'guest' assert obj._get_role_by_affiliation('a@x.com') == 'guest'

View File

@ -2,30 +2,31 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
#At a minimum you will need username, # At a minimum you will need username,
default_shib_attributes = { default_shib_attributes = {
"Shibboleth-eppn": (True, "username"), "Shibboleth-eppn": (True, "username"),
} }
SHIB_ATTRIBUTE_MAP = getattr(settings, 'SHIBBOLETH_ATTRIBUTE_MAP', default_shib_attributes) SHIB_ATTRIBUTE_MAP = getattr(settings, 'SHIBBOLETH_ATTRIBUTE_MAP', default_shib_attributes)
#Set to true if you are testing and want to insert sample headers. # Set to true if you are testing and want to insert sample headers.
SHIB_MOCK_HEADERS = getattr(settings, 'SHIBBOLETH_MOCK_HEADERS', False) SHIB_MOCK_HEADERS = getattr(settings, 'SHIBBOLETH_MOCK_HEADERS', False)
SHIB_USER_HEADER = getattr(settings, 'SHIBBOLETH_USER_HEADER', "REMOTE_USER") SHIB_USER_HEADER = getattr(settings, 'SHIBBOLETH_USER_HEADER', "HTTP_REMOTE_USER")
SHIB_USER_HEADER_SECOND_UID = getattr(settings,
'SHIBBOLETH_USER_HEADER_SECOND_UID',
'HTTP_REMOTE_USER_SUBJECT_ID')
LOGIN_URL = getattr(settings, 'LOGIN_URL', None) LOGIN_URL = getattr(settings, 'LOGIN_URL', None)
if not LOGIN_URL: if not LOGIN_URL:
raise ImproperlyConfigured("A LOGIN_URL is required. Specify in settings.py") raise ImproperlyConfigured("A LOGIN_URL is required. Specify in settings.py")
#Optional logout parameters # Optional logout parameters
#This should look like: https://sso.school.edu/idp/logout.jsp?return=%s # This should look like: https://sso.school.edu/idp/logout.jsp?return=%s
#The return url variable will be replaced in the LogoutView. # The return url variable will be replaced in the LogoutView.
LOGOUT_URL = getattr(settings, 'SHIBBOLETH_LOGOUT_URL', None) LOGOUT_URL = getattr(settings, 'SHIBBOLETH_LOGOUT_URL', None)
#LOGOUT_REDIRECT_URL specifies a default logout page that will always be used when # LOGOUT_REDIRECT_URL specifies a default logout page that will always be used when
#users logout from Shibboleth. # users logout from Shibboleth.
LOGOUT_REDIRECT_URL = getattr(settings, 'SHIBBOLETH_LOGOUT_REDIRECT_URL', None) LOGOUT_REDIRECT_URL = getattr(settings, 'SHIBBOLETH_LOGOUT_REDIRECT_URL', None)
#Name of key. Probably no need to change this. # Name of key. Probably no need to change this.
LOGOUT_SESSION_KEY = getattr(settings, 'SHIBBOLETH_FORCE_REAUTH_SESSION_KEY', 'shib_force_reauth') LOGOUT_SESSION_KEY = getattr(settings, 'SHIBBOLETH_FORCE_REAUTH_SESSION_KEY', 'shib_force_reauth')

View File

@ -38,7 +38,7 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend):
user = None user = None
return user return user
def authenticate(self, remote_user, shib_meta): def authenticate(self, remote_user, shib_meta, second_uid=''):
""" """
The username passed as ``remote_user`` is considered trusted. This The username passed as ``remote_user`` is considered trusted. This
method simply returns the ``User`` object with the given username, method simply returns the ``User`` object with the given username,
@ -69,10 +69,21 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend):
except User.DoesNotExist: except User.DoesNotExist:
user = None user = None
if user and second_uid:
SocialAuthUser.objects.add_if_not_exists(user.username,
SHIBBOLETH_PROVIDER_IDENTIFIER,
second_uid)
if not user and self.create_unknown_user: if not user and self.create_unknown_user:
try: try:
user = User.objects.create_shib_user(is_active=self.activate_after_creation) user = User.objects.create_shib_user(is_active=self.activate_after_creation)
SocialAuthUser.objects.add(user.username, SHIBBOLETH_PROVIDER_IDENTIFIER, remote_user) SocialAuthUser.objects.add_if_not_exists(user.username,
SHIBBOLETH_PROVIDER_IDENTIFIER,
remote_user)
if second_uid:
SocialAuthUser.objects.add_if_not_exists(user.username,
SHIBBOLETH_PROVIDER_IDENTIFIER,
second_uid)
except Exception as e: except Exception as e:
logger.error('create shib user failed: %s' % e) logger.error('create shib user failed: %s' % e)
return None return None

View File

@ -11,7 +11,8 @@ from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from seaserv import seafile_api, ccnet_api from seaserv import seafile_api, ccnet_api
from shibboleth.app_settings import SHIB_ATTRIBUTE_MAP, LOGOUT_SESSION_KEY, SHIB_USER_HEADER from shibboleth.app_settings import SHIB_ATTRIBUTE_MAP, LOGOUT_SESSION_KEY, \
SHIB_USER_HEADER, SHIB_USER_HEADER_SECOND_UID
from seahub import auth from seahub import auth
from seahub.base.accounts import User from seahub.base.accounts import User
@ -40,8 +41,7 @@ SHIBBOLETH_PROVIDER_IDENTIFIER = getattr(settings, 'SHIBBOLETH_PROVIDER_IDENTIFI
class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware): class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
""" """
Authentication Middleware for use with Shibboleth. Uses the recommended pattern Authentication Middleware for use with Shibboleth.
for remote authentication from: http://code.djangoproject.com/svn/django/tags/releases/1.3/django/contrib/auth/middleware.py
""" """
def process_request(self, request): def process_request(self, request):
if request.path.rstrip('/') != settings.SITE_ROOT + 'sso': if request.path.rstrip('/') != settings.SITE_ROOT + 'sso':
@ -74,19 +74,13 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
# AuthenticationMiddleware). # AuthenticationMiddleware).
return return
second_uid = request.META.get(SHIB_USER_HEADER_SECOND_UID, '')
# If the user is already authenticated and that user is the user we are # If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already # getting passed in the headers, then the correct user is already
# persisted in the session and we don't need to continue. # persisted in the session and we don't need to continue.
if request.user.is_authenticated: if request.user.is_authenticated:
# If user is already authenticated, the value of request.user.username should be random ID of user, return
# not the SHIB_USER_HEADER in the request header
shib_user = SocialAuthUser.objects.get_by_provider_and_uid(SHIBBOLETH_PROVIDER_IDENTIFIER, remote_user)
if shib_user:
remote_user = shib_user.username
if request.user.username == remote_user:
if request.user.is_staff:
update_sudo_mode_ts(request)
return
# Make sure we have all required Shiboleth elements before proceeding. # Make sure we have all required Shiboleth elements before proceeding.
shib_meta, error = self.parse_attributes(request) shib_meta, error = self.parse_attributes(request)
@ -98,7 +92,9 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
# We are seeing this user for the first time in this session, attempt # We are seeing this user for the first time in this session, attempt
# to authenticate the user. # to authenticate the user.
user = auth.authenticate(remote_user=remote_user, shib_meta=shib_meta) user = auth.authenticate(remote_user=remote_user,
shib_meta=shib_meta,
second_uid=second_uid)
if user: if user:
if not user.is_active: if not user.is_active:
return HttpResponseRedirect(reverse('shib_complete')) return HttpResponseRedirect(reverse('shib_complete'))