1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-13 23:14:29 +00:00
seahub/thirdpart/shibboleth/middleware.py

266 lines
9.9 KiB
Python
Raw Normal View History

from collections import OrderedDict
from fnmatch import fnmatch
import logging
import os
import sys
2017-01-17 02:38:56 +00:00
from django.conf import settings
2014-12-17 06:29:29 +00:00
from django.contrib.auth.middleware import RemoteUserMiddleware
from django.core.exceptions import ImproperlyConfigured
2020-07-27 06:59:18 +00:00
from django.urls import reverse
2016-03-24 08:25:45 +00:00
from django.http import HttpResponseRedirect
from seaserv import seafile_api, ccnet_api
2014-12-17 06:29:29 +00:00
from shibboleth.app_settings import SHIB_ATTRIBUTE_MAP, LOGOUT_SESSION_KEY, \
SHIB_USER_HEADER, SHIB_USER_HEADER_SECOND_UID
2014-12-17 06:29:29 +00:00
2014-12-17 06:40:16 +00:00
from seahub import auth
2017-01-17 02:38:56 +00:00
from seahub.base.accounts import User
from seahub.profile.models import Profile
from seahub.utils.file_size import get_quota_from_string
from seahub.role_permissions.utils import get_enabled_role_permissions_by_role
2025-01-16 02:00:49 +00:00
from seahub.utils.ccnet_db import CcnetDB
# Get an instance of a logger
logger = logging.getLogger(__name__)
try:
conf_dir = os.environ['SEAFILE_CENTRAL_CONF_DIR']
sys.path.append(conf_dir)
try:
from seahub_custom_functions import custom_shibboleth_get_user_role
CUSTOM_SHIBBOLETH_GET_USER_ROLE = True
except ImportError:
CUSTOM_SHIBBOLETH_GET_USER_ROLE = False
except KeyError:
CUSTOM_SHIBBOLETH_GET_USER_ROLE = False
2014-12-17 06:40:16 +00:00
SHIBBOLETH_PROVIDER_IDENTIFIER = getattr(settings, 'SHIBBOLETH_PROVIDER_IDENTIFIER', 'shibboleth')
2014-12-17 06:40:16 +00:00
2014-12-17 06:29:29 +00:00
class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
"""
Authentication Middleware for use with Shibboleth.
2014-12-17 06:29:29 +00:00
"""
def process_request(self, request):
if request.path.rstrip('/') != settings.SITE_ROOT + 'sso':
return
2014-12-17 06:29:29 +00:00
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
raise ImproperlyConfigured(
"The Django remote user auth middleware requires the"
" authentication middleware to be installed. Edit your"
2020-07-27 06:59:18 +00:00
" MIDDLEWARE setting to insert"
2014-12-17 06:29:29 +00:00
" 'django.contrib.auth.middleware.AuthenticationMiddleware'"
" before the RemoteUserMiddleware class.")
# To support logout. If this variable is True, do not
# authenticate user and return now.
if request.session.get(LOGOUT_SESSION_KEY) is True:
2014-12-17 06:29:29 +00:00
return
else:
# Delete the shib reauth session key if present.
2015-05-05 05:42:03 +00:00
request.session.pop(LOGOUT_SESSION_KEY, None)
2014-12-17 06:29:29 +00:00
# Locate the remote user header.
# import pprint; pprint.pprint(request.META)
2014-12-17 06:29:29 +00:00
try:
remote_user = request.META[SHIB_USER_HEADER]
2014-12-17 06:29:29 +00:00
except KeyError:
# If specified header doesn't exist then return (leaving
# request.user set to AnonymousUser by the
# AuthenticationMiddleware).
return
second_uid = request.META.get(SHIB_USER_HEADER_SECOND_UID, '')
2014-12-17 06:29:29 +00:00
# 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
# persisted in the session and we don't need to continue.
2021-04-21 15:38:53 +00:00
if request.user.is_authenticated:
return
2014-12-17 06:29:29 +00:00
# Make sure we have all required Shiboleth elements before proceeding.
shib_meta, error = self.parse_attributes(request)
# Add parsed attributes to the session.
request.session['shib'] = shib_meta
if error:
raise ShibbolethValidationError("All required Shibboleth elements"
" not found. %s" % shib_meta)
# We are seeing this user for the first time in this session, attempt
# to authenticate the user.
user = auth.authenticate(remote_user=remote_user,
shib_meta=shib_meta,
second_uid=second_uid)
2014-12-17 06:29:29 +00:00
if user:
2016-03-24 08:25:45 +00:00
if not user.is_active:
2016-05-17 09:47:07 +00:00
return HttpResponseRedirect(reverse('shib_complete'))
2016-03-24 08:25:45 +00:00
2014-12-17 06:29:29 +00:00
# User is valid. Set request.user and persist user in the session
# by logging the user in.
request.user = user
auth.login(request, user)
user.set_unusable_password()
user.save()
# call make profile.
self.make_profile(user, shib_meta)
2025-01-16 02:00:49 +00:00
db_api = CcnetDB()
db_user_role = db_api.get_user_role_from_db(user.email)
if db_user_role.is_manual_set:
user_role = db_user_role.role
else:
2025-01-16 02:00:49 +00:00
if CUSTOM_SHIBBOLETH_GET_USER_ROLE:
user_role = custom_shibboleth_get_user_role(shib_meta)
if user_role:
ccnet_api.update_role_emailuser(user.email, user_role, False)
else:
user_role = self.update_user_role(user, shib_meta, False)
else:
user_role = self.update_user_role(user, shib_meta, False)
if user_role:
self.update_user_quota(user, user_role)
# setup session.
2014-12-17 06:29:29 +00:00
self.setup_session(request)
request.shib_login = True
def process_response(self, request, response):
if getattr(request, 'shib_login', False):
Python3 master (#4076) * delete thridpart/social_django * delete social_django in seahub/urls.py * delete social_django in seahub/settings.py * delete seahub/notifications/management/commands/send_wxwork_notices.py * delete social_django in code annotation * delete seahub/social_core * delete tests/seahub/social_core * delete social_core in seahub/urls.py * delete social_core in seahub/settings.py * change app_label to auth in SocialAuthUser model * 2to3 asserts * 2to3 basestring * 2to3 dict * 2to3 except * 2to3 filter * 2to3 future * 2to3 has_key * 2to3 idioms * 2to3 import * 2to3 imports * 2to3 long * 2to3 map * 2to3 next * 2to3 numliterals * 2to3 print * 2to3 raise * 2to3 raw_input * 2to3 reduce * 2to3 reload * 2to3 set_literal * 2to3 unicode * 2to3 urllib * 2to3 ws_comma * 2to3 xrange * 2to3 zip * add pymysql in __init__.py * fix encode and decode in seahub/cconvert.py * fix seafserv_rpc.is_passwd_set in seahub/views/__init__.py * fix smart_unicode to smart_text * fix force_unicode to force_text * delete seaserv.get_session_info * delete seaserv.ccnet_rpc * fix indent error in seahub/auth/middleware.py * update dev-requirements * update test-requirements * update requirements * fix StringIO to BytesIO in thumbnail * fix seaserv.list_inner_pub_repos to seafile_api.get_inner_pub_repo_list * fix seaserv.list_org_inner_pub_repos to seafile_api.list_org_inner_pub_repos * add logger in seahub/utils/__init__.py * fix sort cmp in seahub/views/__init__.py * fix sort cmp in seahub/base/management/commands/export_file_access_log.py * fix sort cmp in seahub/api2/endpoints/repo_trash.py * fix sort cmp in seahub/api2/endpoints/shared_repos.py * fix sort cmp in seahub/api2/endpoints/shared_folders.py * fix sort cmp in seahub/wiki/views.py * fix sort cmp in seahub/api2/endpoints/wiki_pages.py * fix sort cmp in seahub/api2/endpoints/group_libraries.py * fix sort cmp in seahub/base/models.py * fix sort cmp in seahub/api2/endpoints/upload_links.py * fix sort cmp in seahub/views/ajax.py * fix sort cmp in seahub/api2/views.py * fix sort cmp in seahub/views/wiki.py * fix sort cmp in seahub/api2/endpoints/repos.py * fix sort cmp in seahub/api2/endpoints/starred_items.py * fix sort cmp in seahub/views/file.py * fix sort cmp in seahub/api2/endpoints/dir.py * fix sort cmp in seahub/api2/endpoints/share_links.py * fix cmp to cmp_to_key in seahub/api2/endpoints/admin/device_trusted_ip.py * fix cmp to cmp_to_key in tests/api/endpoints/admin/test_device_trusted_ip.py * delete encode('utf-8') in seafile_api.list_dir_by_commit_and_path * delete encode('utf-8') in is_file_starred * delete encode('utf-8') in seafile_api.list_dir_by_path * delete path.encode('utf-8') in seahub/views/file.py * fix os.write to add encode('utf-8') * add encode('utf-8') for hashlib * add encode('utf-8') for hmac * fix with open(file, 'wb') for binary file * fix encode and decode in seahub/utils/hasher.py * fix next in thirdpart/shibboleth/views.py * fix next in seahub/profile/views.py * fix next in seahub/notifications/views.py * fix next in seahub/institutions/views.py * fix next in seahub/options/views.py * fix next in seahub/share/views.py * fix next in seahub/avatar/views.py * fix next in seahub/views/__init__.py * fix next in seahub/group/views.py * fix next in seahub/views/wiki.py * fix next in seahub/views/sysadmin.py * fix next in seahub/views/file.py * fix string.lowercase to string.ascii_lowercase in test * fix open file add 'rb' in test * fix self.user.username in test * add migrations in file_participants * fix list_org_inner_pub_repos to list_org_inner_pub_repos_by_owner * fix from seaserv import is_passwd_set to seafile_api.is_password_set * fix assert bytes resp.content in test * fix seafile_api.get_inner_pub_repo_list to seafile_api.list_inner_pub_repos_by_owner * fix seafile_api.is_passwd_set to seafile_api.is_password_set * fix AccountsApiTest assert length * rewrite sort_devices cmp to operator.lt * fix bytes + str in seahub/api2/views.py * fix assert bytes resp.content in test * fix hashlib encode in seahub/thirdpart/registration/models.py * change app_label to base in SocialAuthUser * fix base64 encode in seahub/base/database_storage/database_storage.py * fix assert bytes resp.content * remove path.decode in def mkstemp() * remove path.decode in FpathToLinkTest * remove str decode in FileTagTest * remove mock_write_xls.assert_called_once() in SysUserAdminExportExcelTest * fix urllib assert in FilesApiTest * fix link fields in FileCommentsTest * fix get_related_users_by_repo() * fix assert list in GetRepoSharedUsersTest * fix create user in AccountTest * fix repeated key in dict seahub/api2/views.py * add drone.yml * update nginx conf in test * update test conf in test * update dist and push after test success * update drone conf to dist and push * fix assert in BeSharedReposTest * fix seafile_api.list_org_inner_pub_repos_by_owner(org_id, username) to seafile_api.list_org_inner_pub_repos(org_id) * fix seafile_api.list_inner_pub_repos_by_owner(username) to seafile_api.get_inner_pub_repo_list() * update pyjwt requirement * update dist branch in drone * add SKIP in dist and push * fix StringIO to BytesIO in seahub/avatar/models.py * fix if org_id > 0 to if org_id and org_id > 0 * remove payment * fix StringIO to BytesIO in seahub/base/database_storage/database_storage.py * fix send_message to seafile_api.publish_event in seahub/drafts/utils.py * fix send_message to seafile_api.publish_event in seahub/api2/views.py * fix send_message to seafile_api.publish_event in seahub/api2/endpoints/repos.py * fix send_message to seafile_api.publish_event in seahub/views/file.py * fix send_message to seafile_api.publish_event in seahub/utils/__init__.py * fix image_file.read encode in seahub/base/database_storage/database_storage.py * fix DatabaseStorageTest * remove .travis.yml * drone branch include master
2019-09-11 03:46:43 +00:00
print('%s: set shibboleth cookie!' % id(self))
self._set_auth_cookie(request, response)
return response
def _set_auth_cookie(self, request, response):
2015-02-26 12:44:48 +00:00
from seahub.api2.utils import get_token_v1, get_token_v2
# generate tokenv2 using information in request params
keys = (
'platform',
'device_id',
'device_name',
'client_version',
'platform_version',
)
if all(['shib_' + key in request.GET for key in keys]):
platform = request.GET['shib_platform']
device_id = request.GET['shib_device_id']
device_name = request.GET['shib_device_name']
client_version = request.GET['shib_client_version']
platform_version = request.GET['shib_platform_version']
token = get_token_v2(
request, request.user.username, platform, device_id,
device_name, client_version, platform_version)
elif all(['shib_' + key not in request.GET for key in keys]):
2015-04-07 02:25:31 +00:00
token = get_token_v1(request.user.username)
2015-02-26 12:44:48 +00:00
else:
return
response.set_cookie('seahub_auth', request.user.username + '@' + token.key)
2014-12-17 06:29:29 +00:00
def make_profile(self, user, shib_meta):
"""
Extrat nickname(givenname surname), contact_email, institution from
Shib attributs, and add those to user profile.
2014-12-17 06:29:29 +00:00
"""
# use `display_name` as nickname in shib_meta first
nickname = shib_meta.get('display_name', None)
if nickname is None:
# otherwise, fallback to givenname plus surname in shib_meta
givenname = shib_meta.get('givenname', '')
surname = shib_meta.get('surname', '')
nickname = "%s %s" % (givenname, surname)
institution = shib_meta.get('institution', None)
contact_email = shib_meta.get('contact_email', None)
p = Profile.objects.get_profile_by_user(user.username)
if not p:
p = Profile(user=user.username)
if nickname.strip(): # set nickname when it's not empty
p.nickname = nickname.encode("iso-8859-1").decode('utf8')
if institution:
p.institution = institution.encode("iso-8859-1").decode('utf8')
if contact_email:
p.contact_email = contact_email
p.save()
2014-12-17 06:29:29 +00:00
def _get_role_by_affiliation(self, affiliation):
2017-01-17 02:38:56 +00:00
try:
role_map = settings.SHIBBOLETH_AFFILIATION_ROLE_MAP
except AttributeError:
return
role = role_map.get(affiliation)
if role:
return role
if role_map.get('patterns') is not None:
joker_map = role_map.get('patterns')
try:
od = OrderedDict(joker_map)
except Exception as e:
logger.error(e)
return
for k in od:
if fnmatch(affiliation, k):
return od[k]
return None
2025-01-16 02:00:49 +00:00
def update_user_role(self, user, shib_meta, is_manual_set):
affiliation = shib_meta.get('affiliation', '')
if not affiliation:
return
2017-01-17 02:38:56 +00:00
for e in affiliation.split(';'):
role = self._get_role_by_affiliation(e)
2017-01-17 02:38:56 +00:00
if role:
2025-01-16 02:00:49 +00:00
User.objects.update_role(user.email, role, is_manual_set)
return role
def update_user_quota(self, user, user_role):
role_quota = get_enabled_role_permissions_by_role(user_role)['role_quota']
if role_quota:
quota = get_quota_from_string(role_quota)
logger.info('Set quota[%d] for user: %s, role[%s]' % (quota, user.username, user_role))
seafile_api.set_role_quota(user_role, quota)
else:
return
2017-01-17 02:38:56 +00:00
2014-12-17 06:29:29 +00:00
def setup_session(self, request):
"""
If you want to add custom code to setup user sessions, you
can extend this.
"""
return
def parse_attributes(self, request):
"""
Parse the incoming Shibboleth attributes.
From: https://github.com/russell/django-shibboleth/blob/master/django_shibboleth/utils.py
Pull the mapped attributes from the apache headers.
"""
shib_attrs = {}
error = False
meta = request.META
Python3 master (#4076) * delete thridpart/social_django * delete social_django in seahub/urls.py * delete social_django in seahub/settings.py * delete seahub/notifications/management/commands/send_wxwork_notices.py * delete social_django in code annotation * delete seahub/social_core * delete tests/seahub/social_core * delete social_core in seahub/urls.py * delete social_core in seahub/settings.py * change app_label to auth in SocialAuthUser model * 2to3 asserts * 2to3 basestring * 2to3 dict * 2to3 except * 2to3 filter * 2to3 future * 2to3 has_key * 2to3 idioms * 2to3 import * 2to3 imports * 2to3 long * 2to3 map * 2to3 next * 2to3 numliterals * 2to3 print * 2to3 raise * 2to3 raw_input * 2to3 reduce * 2to3 reload * 2to3 set_literal * 2to3 unicode * 2to3 urllib * 2to3 ws_comma * 2to3 xrange * 2to3 zip * add pymysql in __init__.py * fix encode and decode in seahub/cconvert.py * fix seafserv_rpc.is_passwd_set in seahub/views/__init__.py * fix smart_unicode to smart_text * fix force_unicode to force_text * delete seaserv.get_session_info * delete seaserv.ccnet_rpc * fix indent error in seahub/auth/middleware.py * update dev-requirements * update test-requirements * update requirements * fix StringIO to BytesIO in thumbnail * fix seaserv.list_inner_pub_repos to seafile_api.get_inner_pub_repo_list * fix seaserv.list_org_inner_pub_repos to seafile_api.list_org_inner_pub_repos * add logger in seahub/utils/__init__.py * fix sort cmp in seahub/views/__init__.py * fix sort cmp in seahub/base/management/commands/export_file_access_log.py * fix sort cmp in seahub/api2/endpoints/repo_trash.py * fix sort cmp in seahub/api2/endpoints/shared_repos.py * fix sort cmp in seahub/api2/endpoints/shared_folders.py * fix sort cmp in seahub/wiki/views.py * fix sort cmp in seahub/api2/endpoints/wiki_pages.py * fix sort cmp in seahub/api2/endpoints/group_libraries.py * fix sort cmp in seahub/base/models.py * fix sort cmp in seahub/api2/endpoints/upload_links.py * fix sort cmp in seahub/views/ajax.py * fix sort cmp in seahub/api2/views.py * fix sort cmp in seahub/views/wiki.py * fix sort cmp in seahub/api2/endpoints/repos.py * fix sort cmp in seahub/api2/endpoints/starred_items.py * fix sort cmp in seahub/views/file.py * fix sort cmp in seahub/api2/endpoints/dir.py * fix sort cmp in seahub/api2/endpoints/share_links.py * fix cmp to cmp_to_key in seahub/api2/endpoints/admin/device_trusted_ip.py * fix cmp to cmp_to_key in tests/api/endpoints/admin/test_device_trusted_ip.py * delete encode('utf-8') in seafile_api.list_dir_by_commit_and_path * delete encode('utf-8') in is_file_starred * delete encode('utf-8') in seafile_api.list_dir_by_path * delete path.encode('utf-8') in seahub/views/file.py * fix os.write to add encode('utf-8') * add encode('utf-8') for hashlib * add encode('utf-8') for hmac * fix with open(file, 'wb') for binary file * fix encode and decode in seahub/utils/hasher.py * fix next in thirdpart/shibboleth/views.py * fix next in seahub/profile/views.py * fix next in seahub/notifications/views.py * fix next in seahub/institutions/views.py * fix next in seahub/options/views.py * fix next in seahub/share/views.py * fix next in seahub/avatar/views.py * fix next in seahub/views/__init__.py * fix next in seahub/group/views.py * fix next in seahub/views/wiki.py * fix next in seahub/views/sysadmin.py * fix next in seahub/views/file.py * fix string.lowercase to string.ascii_lowercase in test * fix open file add 'rb' in test * fix self.user.username in test * add migrations in file_participants * fix list_org_inner_pub_repos to list_org_inner_pub_repos_by_owner * fix from seaserv import is_passwd_set to seafile_api.is_password_set * fix assert bytes resp.content in test * fix seafile_api.get_inner_pub_repo_list to seafile_api.list_inner_pub_repos_by_owner * fix seafile_api.is_passwd_set to seafile_api.is_password_set * fix AccountsApiTest assert length * rewrite sort_devices cmp to operator.lt * fix bytes + str in seahub/api2/views.py * fix assert bytes resp.content in test * fix hashlib encode in seahub/thirdpart/registration/models.py * change app_label to base in SocialAuthUser * fix base64 encode in seahub/base/database_storage/database_storage.py * fix assert bytes resp.content * remove path.decode in def mkstemp() * remove path.decode in FpathToLinkTest * remove str decode in FileTagTest * remove mock_write_xls.assert_called_once() in SysUserAdminExportExcelTest * fix urllib assert in FilesApiTest * fix link fields in FileCommentsTest * fix get_related_users_by_repo() * fix assert list in GetRepoSharedUsersTest * fix create user in AccountTest * fix repeated key in dict seahub/api2/views.py * add drone.yml * update nginx conf in test * update test conf in test * update dist and push after test success * update drone conf to dist and push * fix assert in BeSharedReposTest * fix seafile_api.list_org_inner_pub_repos_by_owner(org_id, username) to seafile_api.list_org_inner_pub_repos(org_id) * fix seafile_api.list_inner_pub_repos_by_owner(username) to seafile_api.get_inner_pub_repo_list() * update pyjwt requirement * update dist branch in drone * add SKIP in dist and push * fix StringIO to BytesIO in seahub/avatar/models.py * fix if org_id > 0 to if org_id and org_id > 0 * remove payment * fix StringIO to BytesIO in seahub/base/database_storage/database_storage.py * fix send_message to seafile_api.publish_event in seahub/drafts/utils.py * fix send_message to seafile_api.publish_event in seahub/api2/views.py * fix send_message to seafile_api.publish_event in seahub/api2/endpoints/repos.py * fix send_message to seafile_api.publish_event in seahub/views/file.py * fix send_message to seafile_api.publish_event in seahub/utils/__init__.py * fix image_file.read encode in seahub/base/database_storage/database_storage.py * fix DatabaseStorageTest * remove .travis.yml * drone branch include master
2019-09-11 03:46:43 +00:00
for header, attr in list(SHIB_ATTRIBUTE_MAP.items()):
2014-12-17 06:29:29 +00:00
required, name = attr
value = meta.get(header, None)
shib_attrs[name] = value
if not value or value == '':
if required:
error = True
return shib_attrs, error
2014-12-17 06:29:29 +00:00
class ShibbolethValidationError(Exception):
pass