1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-11 17:34:56 +00:00

Improve login and create user ()

* add create oauth/ldap/saml user in UserManager

* improve admin add/import users

* improve dingtalk/weixin/work_weixin login/create user

* improve saml login/create user

* improve oauth login/create user

* login ldap user in seahub

* improve invite user

* fix code

* fix github test action

* fix test

* fix saml login

* optimize code

* specify the version of python-ldap

* fix code

* improve code

* add get_old_user

* optimize oauth login code

* optimize code

* remove LDAP_USER_UNIQUE_ID

* remove test_primary_id

* improve authenticate user

* improve saml login

* optimize code
This commit is contained in:
WJH 2023-06-19 13:06:15 +08:00 committed by GitHub
parent 42773a692d
commit 532fa5ef8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 689 additions and 591 deletions

View File

@ -25,7 +25,7 @@ jobs:
- name: gettext for django
run: |
sudo apt-get install gettext python3-wheel libjwt-dev
sudo apt-get install gettext python3-wheel libjwt-dev libsasl2-dev libldap2-dev
sudo rm -rf /usr/lib/python3/dist-packages/pytz/
- name: Build dist branch

View File

@ -20,7 +20,7 @@ jobs:
sudo apt-get install -y uuid-dev intltool libsqlite3-dev build-essential
sudo apt-get install -y libarchive-dev libtool libjansson-dev valac
sudo apt-get install -y libfuse-dev cmake re2c flex sqlite3
sudo apt-get install -y libssl-dev libldap2-dev libonig-dev
sudo apt-get install -y libssl-dev libsasl2-dev libldap2-dev libonig-dev
sudo apt-get install -y libxml2 libxml2-dev libjwt-dev
- name: clone and build

View File

@ -24,3 +24,4 @@ captcha==0.4
openpyxl==3.0.*
Markdown==3.4.*
bleach==5.0.*
python-ldap==3.4.*

View File

@ -24,6 +24,7 @@ from django.contrib.auth.backends import ModelBackend
from seaserv import ccnet_api, seafile_api
from seahub.base.accounts import User
from seahub.auth.models import SocialAuthUser
from seahub.profile.models import Profile, DetailedProfile
from seahub.utils.file_size import get_quota_from_string
from seahub.role_permissions.utils import get_enabled_role_permissions_by_role
@ -31,18 +32,12 @@ from registration.models import notify_admins_on_activate_request, notify_admins
logger = logging.getLogger(__name__)
SAML_PROVIDER_IDENTIFIER = getattr(settings, 'SAML_PROVIDER_IDENTIFIER', '')
SHIBBOLETH_AFFILIATION_ROLE_MAP = getattr(settings, 'SHIBBOLETH_AFFILIATION_ROLE_MAP', False)
class Saml2Backend(ModelBackend):
def get_user(self, username):
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
user = None
return user
def authenticate(self, session_info=None, attribute_mapping=None, create_unknown_user=True, **kwargs):
if session_info is None or attribute_mapping is None:
logger.error('Session info or attribute mapping are None')
@ -54,59 +49,59 @@ class Saml2Backend(ModelBackend):
attributes = session_info['ava']
if not attributes:
logger.error('The attributes dictionary is empty')
logger.warning('The attributes dictionary is empty')
logger.debug('attributes: %s', attributes)
saml_user = None
if session_info.get('name_id'):
logger.debug('name_id: %s', session_info['name_id'])
saml_user = session_info['name_id'].text
else:
logger.error('The nameid is not available. Cannot find user without a nameid.')
if saml_user is None:
logger.error('Could not determine user identifier')
name_id = session_info.get('name_id', '')
if not name_id:
logger.error('The name_id is not available. Could not determine user identifier.')
return None
name_id = name_id.text
main_attribute = self.clean_user_main_attribute(saml_user)
# check if user exist in local ccnet db/ldapimport database
username = main_attribute
local_ccnet_users = ccnet_api.search_emailusers('DB', username, -1, -1)
if not local_ccnet_users:
local_ccnet_users = ccnet_api.search_emailusers('LDAP', username, -1, -1)
if not local_ccnet_users:
if create_unknown_user:
activate_after_creation = getattr(settings, 'SAML_ACTIVATE_USER_AFTER_CREATION', True)
user = User.objects.create_user(email=username, is_active=activate_after_creation)
# create org user
url_prefix = kwargs.get('url_prefix', None)
if url_prefix:
org = ccnet_api.get_org_by_url_prefix(url_prefix)
if org:
org_id = org.org_id
ccnet_api.add_org_user(org_id, username, 0)
if not activate_after_creation:
notify_admins_on_activate_request(username)
elif settings.NOTIFY_ADMIN_AFTER_REGISTRATION:
notify_admins_on_register_complete(username)
else:
saml_user = SocialAuthUser.objects.get_by_provider_and_uid(SAML_PROVIDER_IDENTIFIER, name_id)
if saml_user:
try:
user = User.objects.get(email=saml_user.username)
except User.DoesNotExist:
user = None
if not user:
# Means found user in social_auth_usersocialauth but not found user in EmailUser,
# delete it and recreate one.
logger.warning('The DB data is invalid, delete it and recreate one.')
SocialAuthUser.objects.filter(provider=SAML_PROVIDER_IDENTIFIER, uid=name_id).delete()
else:
user = User.objects.get(email=username)
# compatible with old users via name_id
try:
user = User.objects.get_old_user(name_id, SAML_PROVIDER_IDENTIFIER, name_id)
except User.DoesNotExist:
user = None
if not user and create_unknown_user:
activate_after_creation = getattr(settings, 'SAML_ACTIVATE_USER_AFTER_CREATION', True)
try:
user = User.objects.create_saml_user(is_active=activate_after_creation)
SocialAuthUser.objects.add(user.username, SAML_PROVIDER_IDENTIFIER, name_id)
except Exception as e:
logger.error(f'create saml user failed. {e}')
return None
# create org user
url_prefix = kwargs.get('url_prefix', None)
if url_prefix:
org = ccnet_api.get_org_by_url_prefix(url_prefix)
if org:
org_id = org.org_id
ccnet_api.add_org_user(org_id, user.username, 0)
if not activate_after_creation:
notify_admins_on_activate_request(user.username)
elif settings.NOTIFY_ADMIN_AFTER_REGISTRATION:
notify_admins_on_register_complete(user.username)
if user:
self.make_profile(user, attributes, attribute_mapping)
return user
def clean_user_main_attribute(self, main_attribute):
"""Hook to clean the extracted user-identifying value. No-op by default."""
return main_attribute
def update_user_role(self, user, parse_result):
role = parse_result.get('role', '')
if role:

View File

@ -11,7 +11,7 @@ from django.utils.translation import gettext as _
from seaserv import ccnet_api
from seahub.utils import render_error, get_service_url
from seahub.utils import get_service_url
from seahub.organizations.models import OrgSAMLConfig
from seahub import settings
@ -24,6 +24,7 @@ if ENABLE_ADFS_LOGIN or ENABLE_MULTI_ADFS:
XMLSEC_BINARY_PATH = getattr(settings, 'SAML_XMLSEC_BINARY_PATH', '/usr/bin/xmlsec1')
CERTS_DIR = getattr(settings, 'SAML_CERTS_DIR', '/opt/seafile/seahub-data/certs')
SAML_ATTRIBUTE_MAPPING = getattr(settings, 'SAML_ATTRIBUTE_MAPPING', {})
SAML_PROVIDER_IDENTIFIER = getattr(settings, 'SAML_PROVIDER_IDENTIFIER', '')
def settings_check(func):
@ -33,18 +34,19 @@ def settings_check(func):
logger.error('Feature not enabled.')
error = True
else:
if not XMLSEC_BINARY_PATH or not CERTS_DIR or not SAML_ATTRIBUTE_MAPPING:
if not XMLSEC_BINARY_PATH or not CERTS_DIR or not SAML_ATTRIBUTE_MAPPING or not SAML_PROVIDER_IDENTIFIER:
logger.error('ADFS login relevant settings invalid.')
logger.error('SAML_XMLSEC_BINARY_PATH: %s' % XMLSEC_BINARY_PATH)
logger.error('SAML_CERTS_DIR: %s' % CERTS_DIR)
logger.error('SAML_ATTRIBUTE_MAPPING: %s' % SAML_ATTRIBUTE_MAPPING)
logger.error('SAML_PROVIDER_IDENTIFIER: %s' % SAML_PROVIDER_IDENTIFIER)
error = True
if ENABLE_ADFS_LOGIN and not REMOTE_METADATA_URL:
logger.error('SAML relevant settings invalid.')
logger.error('SAML_REMOTE_METADATA_URL: %s' % REMOTE_METADATA_URL)
error = True
if error:
return render_error(request, _('Error, please contact administrator.'))
raise Exception(_('Error, please contact administrator.'))
return func(request)
return _decorated
@ -66,7 +68,7 @@ def config_settings_loader(request):
if org_id != -1:
org_saml_config = OrgSAMLConfig.objects.get_config_by_org_id(org_id)
if not org_saml_config:
return render_error(request, 'Failed to get org %s saml_config' % org_id)
raise Exception('Failed to get org %s saml_config' % org_id)
# get org remote_metadata_url
remote_metadata_url = org_saml_config.metadata_url

View File

@ -294,6 +294,7 @@ class Account(APIView):
return api_error(status.HTTP_520_OPERATION_FAILED,
'Failed to update user.')
email = user.email
try:
# update account additional info
self._update_account_additional_info(request, email)
@ -324,6 +325,7 @@ class Account(APIView):
return api_error(status.HTTP_520_OPERATION_FAILED,
'Failed to add user.')
email = user.email
try:
# update account additional info
self._update_account_additional_info(request, email)

View File

@ -21,7 +21,6 @@ from seahub.api2.utils import api_error
from seahub.api2.permissions import IsProVersion
from seahub.base.accounts import User
from seahub.utils.auth import gen_user_virtual_id
from seahub.auth.models import SocialAuthUser
from seahub.profile.models import Profile
from seahub.avatar.models import Avatar
@ -210,9 +209,9 @@ class AdminDingtalkUsersBatch(APIView):
})
continue
email = gen_user_virtual_id()
try:
User.objects.create_user(email)
oauth_user = User.objects.create_oauth_user()
email = oauth_user.username
SocialAuthUser.objects.add(email, 'dingtalk', user_id)
success.append({
'userid': user_id,
@ -226,6 +225,7 @@ class AdminDingtalkUsersBatch(APIView):
'name': user.get('name'),
'error_msg': '导入失败'
})
continue
try:
update_dingtalk_user_info(email,
@ -398,9 +398,9 @@ class AdminDingtalkDepartmentsImport(APIView):
email = social_auth_queryset.get(uid=uid).username
else:
# create user
email = gen_user_virtual_id()
try:
User.objects.create_user(email)
oauth_user = User.objects.create_oauth_user()
email = oauth_user.username
SocialAuthUser.objects.add(email, 'dingtalk', uid)
except Exception as e:
logger.error(e)

View File

@ -215,7 +215,7 @@ class AdminOrgUsers(APIView):
# add user to org
# set `is_staff` parameter as `0`
try:
ccnet_api.add_org_user(org_id, email, 0)
ccnet_api.add_org_user(org_id, user.email, 0)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
@ -223,10 +223,10 @@ class AdminOrgUsers(APIView):
name = request.POST.get('name', None)
if name:
Profile.objects.add_or_update(email, name)
Profile.objects.add_or_update(user.email, name)
if config.FORCE_PASSWORD_CHANGE:
UserOptions.objects.set_force_passwd_change(email)
UserOptions.objects.set_force_passwd_change(user.email)
user_info = get_org_user_info(org_id, user)
user_info['active'] = is_active

View File

@ -53,6 +53,7 @@ from seahub.admin_log.models import USER_DELETE, USER_ADD
from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner
from seahub.group.utils import group_id_to_name
from seahub.institutions.models import InstitutionAdmin
from seahub.auth.utils import get_virtual_id_by_email
from seahub.options.models import UserOptions
from seahub.share.models import FileShare, UploadLinkShare
@ -621,11 +622,6 @@ class AdminUsers(APIView):
error_msg = "Name should not include '/'."
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
contact_email = request.data.get('contact_email', None)
if contact_email and not is_valid_email(contact_email):
error_msg = 'contact_email invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
quota_total_mb = request.data.get("quota_total", None)
if quota_total_mb:
try:
@ -646,8 +642,9 @@ class AdminUsers(APIView):
error_msg = 'Failed to set quota: maximum quota is %d MB' % org_quota_mb
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
vid = get_virtual_id_by_email(email)
try:
User.objects.get(email=email)
User.objects.get(email=vid)
user_exist = True
except User.DoesNotExist:
user_exist = False
@ -665,7 +662,7 @@ class AdminUsers(APIView):
try:
user_obj = User.objects.create_user(email, password, is_staff, is_active)
create_user_info(request, email=user_obj.username, role=role,
nickname=name, contact_email=contact_email,
nickname=name, contact_email=email,
quota_total_mb=quota_total_mb)
except Exception as e:
logger.error(e)
@ -687,7 +684,7 @@ class AdminUsers(APIView):
logger.error(str(e))
add_user_tip = _('Successfully added user %(user)s. But email notification can not be sent, because Email service is not properly configured.') % {'user': email}
user_info = get_user_info(email)
user_info = get_user_info(user_obj.username)
user_info['add_user_tip'] = add_user_tip
# send admin operation log signal
@ -698,7 +695,7 @@ class AdminUsers(APIView):
operation=USER_ADD, detail=admin_op_detail)
if config.FORCE_PASSWORD_CHANGE:
UserOptions.objects.set_force_passwd_change(email)
UserOptions.objects.set_force_passwd_change(user_obj.email)
return Response(user_info)

View File

@ -38,6 +38,7 @@ from seahub.base.models import UserLastLogin
from seahub.options.models import UserOptions
from seahub.role_permissions.utils import get_available_roles
from seahub.utils.user_permissions import get_user_role
from seahub.auth.utils import get_virtual_id_by_email
logger = logging.getLogger(__name__)
@ -279,6 +280,7 @@ class AdminImportUsers(APIView):
wb = load_workbook(filename=fs, read_only=True)
except Exception as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
# example file is like:
# Email Password Name(Optional) Role(Optional) Space Quota(MB, Optional) Login ID
@ -317,7 +319,7 @@ class AdminImportUsers(APIView):
continue
if record[1]:
password = record[1].strip()
password = str(record[1]).strip()
if not password:
result['failed'].append({
'email': email,
@ -331,8 +333,9 @@ class AdminImportUsers(APIView):
})
continue
vid = get_virtual_id_by_email(email)
try:
User.objects.get(email=email)
User.objects.get(email=vid)
result['failed'].append({
'email': email,
'error_msg': 'user %s exists.' % email
@ -341,26 +344,28 @@ class AdminImportUsers(APIView):
except User.DoesNotExist:
pass
User.objects.create_user(email, password, is_staff=False, is_active=True)
user = User.objects.create_user(email, password, is_staff=False, is_active=True)
if config.FORCE_PASSWORD_CHANGE:
UserOptions.objects.set_force_passwd_change(email)
UserOptions.objects.set_force_passwd_change(user.email)
# update the user's optional info
# update nikename
if record[2]:
try:
nickname = record[2].strip()
if len(nickname) <= 64 and '/' not in nickname:
Profile.objects.add_or_update(email, nickname, '')
except Exception as e:
logger.error(e)
nickname = email.split('@')[0]
try:
if record[2]:
input_nickname = str(record[2]).strip()
if len(input_nickname) <= 64 and '/' not in input_nickname:
nickname = input_nickname
Profile.objects.add_or_update(user.email, nickname, '')
except Exception as e:
logger.error(e)
# update role
if record[3]:
try:
role = record[3].strip()
if is_pro_version() and role in get_available_roles():
User.objects.update_role(email, role)
User.objects.update_role(user.email, role)
except Exception as e:
logger.error(e)
@ -370,14 +375,14 @@ class AdminImportUsers(APIView):
space_quota_mb = int(record[4])
if space_quota_mb >= 0:
space_quota = int(space_quota_mb) * get_file_size_unit('MB')
seafile_api.set_user_quota(email, space_quota)
seafile_api.set_user_quota(user.email, space_quota)
except Exception as e:
logger.error(e)
# login id
if record[5]:
try:
Profile.objects.add_or_update(email, login_id=record[5])
Profile.objects.add_or_update(user.email, login_id=record[5])
except Exception as e:
logger.error(e)
@ -390,12 +395,10 @@ class AdminImportUsers(APIView):
'password': password
})
user = User.objects.get(email=email)
info = {}
info['email'] = email
info['name'] = email2nickname(email)
info['contact_email'] = email2contact_email(email)
info = dict()
info['email'] = user.email
info['name'] = email2nickname(user.email)
info['contact_email'] = email2contact_email(user.email)
info['is_staff'] = user.is_staff
info['is_active'] = user.is_active

View File

@ -23,7 +23,6 @@ from seahub.work_weixin.utils import handler_work_weixin_api_response, \
from seahub.work_weixin.settings import WORK_WEIXIN_DEPARTMENTS_URL, \
WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, WORK_WEIXIN_PROVIDER, WORK_WEIXIN_UID_PREFIX
from seahub.base.accounts import User
from seahub.utils.auth import gen_user_virtual_id
from seahub.auth.models import SocialAuthUser
from seahub.group.utils import validate_group_name
from seahub.auth.models import ExternalDepartment
@ -161,12 +160,13 @@ def _handler_work_weixin_user_data(api_user, social_auth_queryset):
return error_data
def _import_user_from_work_weixin(email, api_user):
api_user['username'] = email
def _import_user_from_work_weixin(api_user):
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid')
try:
User.objects.create_user(email)
SocialAuthUser.objects.add(email, WORK_WEIXIN_PROVIDER, uid)
contact_email = api_user.get('contact_email') if api_user.get('contact_email') else None
user = User.objects.create_oauth_user(contact_email)
api_user['username'] = user.username
SocialAuthUser.objects.add(user.username, WORK_WEIXIN_PROVIDER, uid)
update_work_weixin_user_info(api_user)
except Exception as e:
logger.error(e)
@ -202,12 +202,11 @@ class AdminWorkWeixinUsersBatch(APIView):
error_data = _handler_work_weixin_user_data(api_user, social_auth_queryset)
if not error_data:
email = gen_user_virtual_id()
if _import_user_from_work_weixin(email, api_user):
if _import_user_from_work_weixin(api_user):
success.append({
'userid': api_user.get('userid'),
'name': api_user.get('name'),
'email': email,
'email': api_user.get('username'),
})
else:
failed.append({
@ -334,7 +333,7 @@ class AdminWorkWeixinDepartmentsImport(APIView):
if api_department_list is None:
error_msg = '获取企业微信组织架构失败'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
api_department_list = sorted(api_department_list, key=lambda x:x['id'])
api_department_list = sorted(api_department_list, key=lambda x: x['id'])
# list department members from work weixin
api_user_list = self._list_department_members_from_work_weixin(access_token, department_id)
@ -415,16 +414,17 @@ class AdminWorkWeixinDepartmentsImport(APIView):
# determine the user exists
if social_auth_queryset.filter(uid=uid).exists():
email = social_auth_queryset.get(uid=uid).username
email = social_auth_queryset.get(uid=uid).username # this email means username
else:
# create user
email = gen_user_virtual_id()
create_user_success = _import_user_from_work_weixin(email, api_user)
create_user_success = _import_user_from_work_weixin(api_user)
if not create_user_success:
failed_msg = self._api_user_failed_msg(
'', api_user_name, department_id, '导入用户失败')
failed.append(failed_msg)
continue
# api_user's username is from `User.objects.create_oauth_user` in `_import_user_from_work_weixin`
email = api_user.get('username')
# bind user to department
api_user_department_list = api_user.get('department')

View File

@ -12,6 +12,7 @@ from seahub.api2.permissions import CanInviteGuest
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from seahub.base.accounts import User
from seahub.auth.utils import get_virtual_id_by_email
from seahub.utils import is_valid_email
from seahub.invitations.models import Invitation
from seahub.invitations.utils import block_accepter
@ -58,8 +59,9 @@ class InvitationsView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST,
_('%s is already invited.') % accepter)
vid = get_virtual_id_by_email(accepter)
try:
user = User.objects.get(accepter)
user = User.objects.get(vid)
# user is active return exist
if user.is_active is True:
return api_error(status.HTTP_400_BAD_REQUEST,
@ -128,8 +130,9 @@ class InvitationsBatchView(APIView):
continue
vid = get_virtual_id_by_email(accepter)
try:
user = User.objects.get(accepter)
user = User.objects.get(vid)
# user is active return exist
if user.is_active is True:
result['failed'].append({

View File

@ -45,7 +45,7 @@ class RemoteUserBackend(object):
# Create a User object if not already in the database?
create_unknown_user = True
def authenticate(self, request, remote_user):
def authenticate(self, *args, **kwargs):
raise NotImplementedError('authenticate() must be overridden')
def get_user(self, user_id):
@ -149,13 +149,13 @@ class SeafileRemoteUserBackend(AuthBackend):
return None
try:
user = User.objects.create_user(email=username,
is_active=self.auto_activate)
user = User.objects.create_remote_user(email=username,
is_active=self.auto_activate)
if not self.auto_activate:
notify_admins_on_activate_request(username)
notify_admins_on_activate_request(user.username)
elif settings.NOTIFY_ADMIN_AFTER_REGISTRATION:
notify_admins_on_register_complete(username)
notify_admins_on_register_complete(user.username)
except Exception as e:
logger.error(e)
@ -170,7 +170,7 @@ class SeafileRemoteUserBackend(AuthBackend):
return None
# get user again with updated extra info after configure
return self.get_user(username)
return self.get_user(user.username)
def clean_username(self, username):
"""

View File

@ -5,8 +5,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.http import int_to_base36
from collections import OrderedDict
from seaserv import ccnet_api
from seahub.base.accounts import User
from seahub.base.templatetags.seahub_tags import email2contact_email
from seahub.auth import authenticate
@ -14,7 +12,8 @@ from seahub.auth.tokens import default_token_generator
from seahub.options.models import UserOptions
from seahub.profile.models import Profile
from seahub.utils import IS_EMAIL_CONFIGURED, send_html_email, \
is_ldap_user, is_user_password_strong, get_site_name, is_valid_email
is_ldap_user, is_user_password_strong, get_site_name
from seahub.auth.utils import get_virtual_id_by_email
from captcha.fields import CaptchaField
@ -39,12 +38,6 @@ class AuthenticationForm(forms.Form):
self.user_cache = None
super(AuthenticationForm, self).__init__(*args, **kwargs)
def get_primary_id_by_username(self, username):
"""Get user's primary id in case the username is changed.
"""
p_id = ccnet_api.get_primary_id(username)
return p_id if p_id is not None else username
def clean_login(self):
return self.cleaned_data['login'].strip()
@ -53,27 +46,22 @@ class AuthenticationForm(forms.Form):
password = self.cleaned_data.get('password')
if username and password:
if not is_valid_email(username):
# convert login id to username if any
username = Profile.objects.convert_login_str_to_username(username)
self.user_cache = authenticate(username=username,
password=password)
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
"""then try login id/contact email/primary id"""
# convert contact email to username if any
# convert login id or contact email to username if any
username = Profile.objects.convert_login_str_to_username(username)
# convert username to primary id if any
username = self.get_primary_id_by_username(username)
self.user_cache = authenticate(username=username, password=password)
# After local user authentication process is completed, authenticate LDAP user
if self.user_cache is None and settings.ENABLE_LDAP:
self.user_cache = authenticate(ldap_user=username, password=password)
if self.user_cache is None:
err_msg = _("Please enter a correct email/username and password. Note that both fields are case-sensitive.")
if settings.LOGIN_ERROR_DETAILS:
try:
u = User.objects.get(email=username)
User.objects.get(email=username)
except User.DoesNotExist:
err_msg = _("That e-mail address doesn't have an associated user account. Are you sure you've registered?")
self.errors['not_found'] = err_msg
@ -122,10 +110,11 @@ class PasswordResetForm(forms.Form):
raise forms.ValidationError(_('Failed to send email, email service is not properly configured, please contact administrator.'))
email = self.cleaned_data["email"].lower().strip()
vid = get_virtual_id_by_email(email)
# TODO: add filter method to UserManager
try:
self.users_cache = User.objects.get(email=email)
self.users_cache = User.objects.get(email=vid)
except User.DoesNotExist:
raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))

View File

@ -1,7 +1,6 @@
# Copyright (c) 2012-2016 Seafile Ltd.
from django.core.cache import cache
from django.conf import settings
from urllib.parse import quote
from seahub.profile.models import Profile
from seahub.utils import normalize_cache_key
@ -9,6 +8,7 @@ from seahub.utils.ip import get_remote_ip
LOGIN_ATTEMPT_PREFIX = 'UserLoginAttempt_'
def get_login_failed_attempts(username=None, ip=None):
"""Get login failed attempts base on username and ip.
If both username and ip are provided, return the max value.
@ -32,6 +32,7 @@ def get_login_failed_attempts(username=None, ip=None):
return max(username_attempts, ip_attempts)
def incr_login_failed_attempts(username=None, ip=None):
"""Increase login failed attempts by 1 for both username and ip.
@ -61,6 +62,7 @@ def incr_login_failed_attempts(username=None, ip=None):
return max(username_attempts, ip_attempts)
def clear_login_failed_attempts(request, username):
"""Clear login failed attempts records.
@ -74,3 +76,11 @@ def clear_login_failed_attempts(request, username):
p = Profile.objects.get_profile_by_user(username)
if p and p.login_id:
cache.delete(normalize_cache_key(p.login_id, prefix=LOGIN_ATTEMPT_PREFIX))
def get_virtual_id_by_email(email):
p = Profile.objects.get_profile_by_contact_email(email)
if p is None:
return email
else:
return p.user

View File

@ -10,6 +10,7 @@ from seahub.base.accounts import User
from seahub.avatar.settings import AVATAR_DEFAULT_URL, AVATAR_MAX_AVATARS_PER_USER
from seahub.avatar.util import get_primary_avatar
from seahub.avatar.models import Avatar
from seahub.test_utils import Fixtures
try:
from PIL import Image
@ -26,13 +27,13 @@ def upload_helper(o, filename):
f.close()
return response
class AvatarTestCase(TestCase):
class AvatarTestCase(TestCase, Fixtures):
"""
Helper base class for all the follow test cases.
"""
def setUp(self):
self.testdatapath = os.path.join(os.path.dirname(__file__), "testdata")
self.user = User.objects.create_user('lennon@thebeatles.com', 'testpassword', is_active=True)
self.user = self.create_user('lennon@thebeatles.com', 'testpassword', is_active=True)
response = self.client.post('/accounts/login/', {
'username': 'lennon@thebeatles.com',

View File

@ -14,6 +14,9 @@ from seaserv import ccnet_threaded_rpc, unset_repo_passwd, \
seafile_api, ccnet_api
from constance import config
from registration import signals
import ldap
from ldap import sasl
from ldap import filter
from seahub.auth import login
from seahub.constants import DEFAULT_USER, DEFAULT_ORG, DEFAULT_ADMIN
@ -26,6 +29,8 @@ from seahub.utils import is_user_password_strong, get_site_name, \
from seahub.utils.mail import send_html_email_with_dj_template
from seahub.utils.licenseparse import user_number_over_limit
from seahub.share.models import ExtraSharePermission
from seahub.utils.auth import gen_user_virtual_id
from seahub.auth.models import SocialAuthUser
try:
from seahub.settings import CLOUD_MODE
@ -36,6 +41,22 @@ try:
except ImportError:
MULTI_TENANCY = False
from seahub.settings import ENABLE_LDAP, LDAP_USER_FIRST_NAME_ATTR, LDAP_USER_LAST_NAME_ATTR, \
LDAP_USER_NAME_REVERSE, LDAP_FILTER, LDAP_CONTACT_EMAIL_ATTR, LDAP_USER_ROLE_ATTR, \
ACTIVATE_USER_WHEN_IMPORT, ENABLE_SASL, SASL_MECHANISM, SASL_AUTHC_ID_ATTR
try:
from seahub.settings import LDAP_SERVER_URL, LDAP_BASE_DN, LDAP_ADMIN_DN, LDAP_ADMIN_PASSWORD, \
LDAP_PROVIDER, LDAP_LOGIN_ATTR
except ImportError:
LDAP_SERVER_URL = ''
LDAP_BASE_DN = ''
LDAP_ADMIN_DN = ''
LDAP_ADMIN_PASSWORD = ''
LDAP_PROVIDER = ''
LDAP_LOGIN_ATTR = ''
LDAP_UPDATE_USER_WHEN_LOGIN = getattr(settings, 'LDAP_UPDATE_USER_WHEN_LOGIN', True)
logger = logging.getLogger(__name__)
ANONYMOUS_EMAIL = 'Anonymous'
@ -49,9 +70,90 @@ class UserManager(object):
"""
Creates and saves a User with given username and password.
"""
virtual_id = gen_user_virtual_id()
# Lowercasing email address to avoid confusion.
email = email.lower()
user = User(email=virtual_id)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
# Set email as contact email.
Profile.objects.add_or_update(username=virtual_id, contact_email=email)
return self.get(email=virtual_id)
def update_role(self, email, role):
"""
If user has a role, update it; or create a role for user.
"""
ccnet_api.update_role_emailuser(email, role)
return self.get(email=email)
def create_oauth_user(self, email=None, password=None, is_staff=False, is_active=False):
"""
Creates and saves an oauth user which can without email.
"""
virtual_id = gen_user_virtual_id()
user = User(email=virtual_id)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
# Set email as contact email.
if email:
email = email.lower()
Profile.objects.add_or_update(username=virtual_id, contact_email=email)
return self.get(email=virtual_id)
def create_ldap_user(self, email=None, password=None, nickname=None, is_staff=False, is_active=False):
"""
Creates and saves a ldap user which can without email.
"""
virtual_id = gen_user_virtual_id()
user = User(email=virtual_id)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
# Set email as contact email.
if email:
email = email.lower()
Profile.objects.add_or_update(username=virtual_id, contact_email=email, nickname=nickname)
return self.get(email=virtual_id)
def create_saml_user(self, email=None, password=None, nickname=None, is_staff=False, is_active=False):
"""
Creates and saves a saml user which can without email.
"""
virtual_id = gen_user_virtual_id()
user = User(email=virtual_id)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
# Set email as contact email.
if email:
email = email.lower()
Profile.objects.add_or_update(username=virtual_id, contact_email=email, nickname=nickname)
return self.get(email=virtual_id)
def create_remote_user(self, email, password=None, is_staff=False, is_active=False):
"""
Creates and saves a remote user with given username.
"""
user = User(email=email)
user.is_staff = is_staff
user.is_active = is_active
@ -60,11 +162,40 @@ class UserManager(object):
return self.get(email=email)
def update_role(self, email, role):
def create_cas_user(self, email, password=None, is_staff=False, is_active=False):
"""
If user has a role, update it; or create a role for user.
Creates and saves a CAS user with given username.
"""
ccnet_api.update_role_emailuser(email, role)
user = User(email=email)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
return self.get(email=email)
def create_krb_user(self, email, password=None, is_staff=False, is_active=False):
"""
Creates and saves a KRB5 user with given username.
"""
user = User(email=email)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
return self.get(email=email)
def create_shib_user(self, email, password=None, is_staff=False, is_active=False):
"""
Creates and saves a SHIB user with given username.
"""
user = User(email=email)
user.is_staff = is_staff
user.is_active = is_active
user.set_password(password)
user.save()
return self.get(email=email)
def create_superuser(self, email, password):
@ -122,6 +253,43 @@ class UserManager(object):
return user
def get_old_user(self, email, provider, uid):
if not email:
raise User.DoesNotExist('User matching query does not exits.')
emailuser = ccnet_threaded_rpc.get_emailuser(email)
if not emailuser:
raise User.DoesNotExist('User matching query does not exits.')
try:
SocialAuthUser.objects.add(emailuser.email, provider, uid)
except Exception as e:
logger.error(e)
user = User(emailuser.email)
user.id = emailuser.id
user.enc_password = emailuser.password
user.is_staff = emailuser.is_staff
user.is_active = emailuser.is_active
user.ctime = emailuser.ctime
user.org = emailuser.org
user.source = emailuser.source
user.role = emailuser.role
user.reference_id = emailuser.reference_id
if user.is_staff:
try:
role_obj = AdminRole.objects.get_admin_role(emailuser.email)
admin_role = role_obj.role
except AdminRole.DoesNotExist:
admin_role = DEFAULT_ADMIN
user.admin_role = admin_role
else:
user.admin_role = ''
return user
class UserPermissions(object):
def __init__(self, user):
@ -583,6 +751,176 @@ class AuthBackend(object):
return user
def parse_ldap_res(ldap_search_result):
first_name = ''
last_name = ''
contact_email = ''
user_role = ''
authc_id = ''
dn = ldap_search_result[0][0]
first_name_list = ldap_search_result[0][1].get(LDAP_USER_FIRST_NAME_ATTR, [])
last_name_list = ldap_search_result[0][1].get(LDAP_USER_LAST_NAME_ATTR, [])
contact_email_list = ldap_search_result[0][1].get(LDAP_CONTACT_EMAIL_ATTR, [])
user_role_list = ldap_search_result[0][1].get(LDAP_USER_ROLE_ATTR, [])
authc_id_list = list()
if ENABLE_SASL and SASL_MECHANISM:
authc_id_list = ldap_search_result[0][1].get(SASL_AUTHC_ID_ATTR, [])
if first_name_list:
first_name = first_name_list[0].decode()
if last_name_list:
last_name = last_name_list[0].decode()
if LDAP_USER_NAME_REVERSE:
nickname = last_name + ' ' + first_name
else:
nickname = first_name + ' ' + last_name
if contact_email_list:
contact_email = contact_email_list[0].decode()
if user_role_list:
user_role = user_role_list[0].decode()
if authc_id_list:
authc_id = authc_id_list[0].decode()
return dn, nickname, contact_email, user_role, authc_id
class CustomLDAPBackend(object):
""" A custom LDAP authentication backend """
def get_user(self, username):
try:
user = User.objects.get(username)
except User.DoesNotExist:
user = None
return user
def authenticate(self, ldap_user=None, password=None):
if not is_pro_version() or not ENABLE_LDAP:
return
admin_bind_conn = ldap.initialize(LDAP_SERVER_URL)
try:
admin_bind_conn.set_option(ldap.OPT_REFERRALS, 0)
except Exception as e:
logger.error(f'Failed to set referrals option. {e}')
return
try:
admin_bind_conn.protocol_version = ldap.VERSION3
if ENABLE_SASL and SASL_MECHANISM:
sasl_cb_value_dict = {}
if SASL_MECHANISM != 'EXTERNAL' and SASL_MECHANISM != 'GSSAPI':
sasl_cb_value_dict = {
sasl.CB_AUTHNAME: LDAP_ADMIN_DN,
sasl.CB_PASS: LDAP_ADMIN_PASSWORD,
}
sasl_auth = sasl.sasl(sasl_cb_value_dict, SASL_MECHANISM)
admin_bind_conn.sasl_interactive_bind_s('', sasl_auth)
else:
admin_bind_conn.simple_bind_s(LDAP_ADMIN_DN, LDAP_ADMIN_PASSWORD)
except Exception as e:
logger.error(f'ldap admin bind failed. {e}')
return
username = Profile.objects.convert_login_str_to_username(ldap_user)
auth_user = SocialAuthUser.objects.filter(username=username, provider=LDAP_PROVIDER).first()
if auth_user:
login_attr = auth_user.uid
else:
login_attr = username
if LDAP_LOGIN_ATTR.lower() in ['email', 'mail']:
filterstr = filter.filter_format('(&(mail=%s))', [login_attr])
else:
filterstr = filter.filter_format(f'(&({LDAP_LOGIN_ATTR}=%s))', [login_attr])
if LDAP_FILTER:
filterstr = filterstr[:-1] + '(' + LDAP_FILTER + '))'
try:
result_data = admin_bind_conn.search_s(LDAP_BASE_DN, ldap.SCOPE_SUBTREE, filterstr)
except Exception as e:
logger.error(f'ldap user search failed. {e}')
return
# user not found in ldap
if not result_data:
logger.error(f'ldap user {login_attr} not found.')
return
# delete old ldap connection instance and create new, if not, some err will occur
admin_bind_conn.unbind_s()
del admin_bind_conn
try:
dn, nickname, contact_email, user_role, authc_id = parse_ldap_res(result_data)
except Exception as e:
logger.error(f'parse ldap result failed {e}')
return
user_bind_conn = ldap.initialize(LDAP_SERVER_URL)
try:
user_bind_conn.protocol_version = ldap.VERSION3
if ENABLE_SASL and SASL_MECHANISM:
sasl_cb_value_dict = {}
if SASL_MECHANISM != 'EXTERNAL' and SASL_MECHANISM != 'GSSAPI':
sasl_cb_value_dict = {
sasl.CB_AUTHNAME: authc_id,
sasl.CB_PASS: password,
}
sasl_auth = sasl.sasl(sasl_cb_value_dict, SASL_MECHANISM)
user_bind_conn.sasl_interactive_bind_s('', sasl_auth)
else:
user_bind_conn.simple_bind_s(dn, password)
except Exception as e:
logger.error(f'ldap user bind failed. {e}')
return
user_bind_conn.unbind_s()
# check if existed
ldap_user = SocialAuthUser.objects.filter(provider=LDAP_PROVIDER, uid=login_attr).first()
if ldap_user:
user = self.get_user(ldap_user.username)
if not user:
# Means found user in social_auth_usersocialauth but not found user in EmailUser,
# delete it and recreate one.
logger.warning('The DB data is invalid, delete it and recreate one.')
SocialAuthUser.objects.filter(provider=LDAP_PROVIDER, uid=login_attr).delete()
else:
# compatible with old users
try:
user = User.objects.get_old_user(username, LDAP_PROVIDER, login_attr)
except User.DoesNotExist:
user = None
if not user:
try:
user = User.objects.create_ldap_user(is_active=ACTIVATE_USER_WHEN_IMPORT)
SocialAuthUser.objects.add(user.username, LDAP_PROVIDER, login_attr)
except Exception as e:
logger.error(f'recreate ldap user failed. {e}')
return
username = user.username
if LDAP_UPDATE_USER_WHEN_LOGIN:
profile_kwargs = {
'nickname': nickname,
'contact_email': contact_email,
}
try:
Profile.objects.add_or_update(username, **profile_kwargs)
except Exception as e:
logger.error(f'update ldap user failed {e}')
if user_role:
User.objects.update_role(username, user_role)
return user
# Register related
class RegistrationBackend(object):
"""

View File

@ -18,12 +18,12 @@ from seahub.api2.utils import get_api_token
from seahub import auth
from seahub.profile.models import Profile
from seahub.utils import render_error, get_site_scheme_and_netloc
from seahub.utils.auth import gen_user_virtual_id, VIRTUAL_ID_EMAIL_DOMAIN
from seahub.utils.auth import VIRTUAL_ID_EMAIL_DOMAIN
from seahub.base.accounts import User
from seahub.auth.models import SocialAuthUser
from seahub.auth.decorators import login_required
from seahub.dingtalk.utils import dingtalk_get_detailed_user_info, \
dingtalk_get_orgapp_token, dingtalk_get_userid_by_unionid_new, \
dingtalk_get_userid_by_unionid_new, \
dingtalk_get_detailed_user_info_new
from seahub.dingtalk.settings import ENABLE_DINGTALK
@ -107,12 +107,14 @@ def dingtalk_callback(request):
auth_user = SocialAuthUser.objects.get_by_provider_and_uid('dingtalk', user_info['unionid'])
if auth_user:
email = auth_user.username
is_new_user = False
else:
email = gen_user_virtual_id()
SocialAuthUser.objects.add(email, 'dingtalk', user_info['unionid'])
email = None
is_new_user = True
try:
user = auth.authenticate(remote_user=email)
email = user.username
except User.DoesNotExist:
user = None
except Exception as e:
@ -122,6 +124,10 @@ def dingtalk_callback(request):
if not user or not user.is_active:
return render_error(request, _('User %s not found or inactive.') % email)
# bind
if is_new_user:
SocialAuthUser.objects.add(user.username, 'dingtalk', user_info['unionid'])
# User is valid. Set request.user and persist user in the session
# by logging the user in.
request.user = user
@ -130,18 +136,13 @@ def dingtalk_callback(request):
# update user's profile
name = user_info['nick'] if 'nick' in user_info else ''
if name:
user_detail_info = dingtalk_get_detailed_user_info(user_info['unionid'])
contact_email = user_detail_info.get('email', '')
if name or contact_email:
profile = Profile.objects.get_profile_by_user(email)
if not profile:
profile = Profile(user=email)
profile.nickname = name.strip()
profile.save()
user_detail_info = dingtalk_get_detailed_user_info(user_info['unionid'])
contact_email = user_detail_info.get('email', '')
if contact_email:
profile.contact_email = contact_email
profile.save()
@ -343,12 +344,14 @@ def dingtalk_callback_new(request):
auth_user = SocialAuthUser.objects.get_by_provider_and_uid('dingtalk', union_id)
if auth_user:
email = auth_user.username
is_new_user = False
else:
email = gen_user_virtual_id()
SocialAuthUser.objects.add(email, 'dingtalk', union_id)
email = None
is_new_user = True
try:
user = auth.authenticate(remote_user=email)
email = user.username
except User.DoesNotExist:
user = None
except Exception as e:
@ -358,6 +361,10 @@ def dingtalk_callback_new(request):
if not user or not user.is_active:
return render_error(request, _('User %s not found or inactive.') % email)
# bind
if is_new_user:
SocialAuthUser.objects.add(user.username, 'dingtalk', union_id)
# User is valid. Set request.user and persist user in the session
# by logging the user in.
request.user = user

View File

@ -70,7 +70,7 @@ class CASBackend(ModelBackend):
user = User.objects.get(email=username)
created = False
except User.DoesNotExist:
user = User.objects.create_user(
user = User.objects.create_cas_user(
email=username, is_active=True)
user = self.configure_user(user)
created = True

View File

@ -8,14 +8,15 @@ from django.test import TestCase, Client
from group.models import GroupMessage
from base.accounts import User
from notifications.models import UserNotification
from seahub.test_utils import Fixtures
class GroupTestCase(TestCase):
class GroupTestCase(TestCase, Fixtures):
"""
Helper base class for all the follow test cases.
"""
def setUp(self):
self.testdatapath = os.path.join(os.path.dirname(__file__), "testdata")
self.user = User.objects.create_user('lennon@thebeatles.com', 'testpassword', is_active=True)
self.user = self.create_user('lennon@thebeatles.com', 'testpassword', is_active=True)
# Login user
response = self.client.post('/accounts/login/', {

View File

@ -10,6 +10,7 @@ from seaserv import seafile_api
from seahub.auth import login as auth_login, authenticate
from seahub.auth import get_backends
from seahub.auth.utils import get_virtual_id_by_email
from seahub.base.accounts import User
from seahub.constants import GUEST_USER
from seahub.invitations.models import Invitation, RepoShareInvitation
@ -29,9 +30,10 @@ def token_view(request, token):
if i.is_expired():
raise Http404
vid = get_virtual_id_by_email(i.accepter)
if request.method == 'GET':
try:
user = User.objects.get(email=i.accepter)
user = User.objects.get(email=vid)
if user.is_active is True:
# user is active return exist
messages.error(request, _('A user with this email already exists.'))
@ -46,7 +48,7 @@ def token_view(request, token):
return HttpResponseRedirect(request.headers.get('referer'))
try:
user = User.objects.get(email=i.accepter)
user = User.objects.get(email=vid)
if user.is_active is True:
# user is active return exist
messages.error(request, _('A user with this email already exists.'))
@ -83,7 +85,7 @@ def token_view(request, token):
# repo share invitation
try:
shared_queryset = RepoShareInvitation.objects.list_by_invitation(invitation=i)
accepter = i.accepter
accepter = user.username
for shared_obj in shared_queryset:
repo_id = shared_obj.repo_id

View File

@ -48,8 +48,8 @@ class RemoteKrbBackend(RemoteUserBackend):
user = self.get_user(username)
if not user:
if self.create_unknown_user:
user = User.objects.create_user(email=username,
is_active=True)
user = User.objects.create_krb_user(email=username,
is_active=True)
else:
pass

View File

@ -58,21 +58,17 @@ class OauthRemoteUserBackend(RemoteUserBackend):
Returns None if ``create_unknown_user`` is ``False`` and a ``User``
object with the given username is not found in the database.
"""
if not remote_user:
return
if remote_user:
username = self.clean_username(remote_user)
user = self.get_user(username)
else:
user = None
username = self.clean_username(remote_user)
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
if self.create_unknown_user:
user = User.objects.create_user(
email=username, is_active=self.activate_after_creation)
if not self.activate_after_creation:
notify_admins_on_activate_request(username)
elif settings.NOTIFY_ADMIN_AFTER_REGISTRATION:
notify_admins_on_register_complete(username)
else:
user = None
if not user and self.create_unknown_user:
user = User.objects.create_oauth_user(is_active=self.activate_after_creation)
if not self.activate_after_creation:
notify_admins_on_activate_request(user.username)
elif settings.NOTIFY_ADMIN_AFTER_REGISTRATION:
notify_admins_on_register_complete(user.username)
return user

View File

@ -15,6 +15,7 @@ from seahub.utils import is_valid_email, render_error, get_service_url
from seahub.utils.file_size import get_quota_from_string
from seahub.base.accounts import User
from seahub.role_permissions.utils import get_enabled_role_permissions_by_role
from seahub.auth.models import SocialAuthUser
import seahub.settings as settings
logger = logging.getLogger(__name__)
@ -50,10 +51,7 @@ if ENABLE_OAUTH:
# Used for init an user for Seahub.
PROVIDER_DOMAIN = getattr(settings, 'OAUTH_PROVIDER_DOMAIN', '')
ATTRIBUTE_MAP = {
'id': (True, "email"),
}
ATTRIBUTE_MAP.update(getattr(settings, 'OAUTH_ATTRIBUTE_MAP', {}))
OAUTH_ATTRIBUTE_MAP = getattr(settings, 'OAUTH_ATTRIBUTE_MAP', {})
def oauth_check(func):
@ -154,36 +152,46 @@ def oauth_callback(request):
logger.error(e)
return render_error(request, _('Error, please contact administrator.'))
def format_user_info(user_info_resp):
logger.info('user info resp: %s' % user_info_resp.text)
error = False
user_info = {}
user_info_json = user_info_resp.json()
oauth_user_info = {}
user_info_json = user_info_resp.json()
for oauth_attr, attr_tuple in OAUTH_ATTRIBUTE_MAP.items():
required, user_attr = attr_tuple
attr_value = user_info_json.get(oauth_attr, '')
if attr_value:
oauth_user_info[user_attr] = attr_value
elif required:
logger.error('Required user attr not found.')
logger.error(user_info_json)
return render_error(request, _('Error, please contact administrator.'))
for item, attr in list(ATTRIBUTE_MAP.items()):
required, user_attr = attr
value = user_info_json.get(item, '')
if value:
# ccnet email
if user_attr == 'email':
user_info[user_attr] = value if is_valid_email(str(value)) else \
'%s@%s' % (str(value), PROVIDER_DOMAIN)
else:
user_info[user_attr] = value
elif required:
error = True
return user_info, error
user_info, error = format_user_info(user_info_resp)
if error:
logger.error('Required user info not found.')
logger.error(user_info)
uid = oauth_user_info.get('uid', '') or oauth_user_info.get('email', '')
if not uid:
logger.error('oauth user uid and email not found.')
logger.error('user_info_json: %s' % user_info_json)
return render_error(request, _('Error, please contact administrator.'))
# seahub authenticate user
email = user_info['email']
# compatible with old users via email
old_email = oauth_user_info.get('email', '')
oauth_user = SocialAuthUser.objects.get_by_provider_and_uid(PROVIDER_DOMAIN, uid)
if oauth_user:
email = oauth_user.username
is_new_user = False
elif old_email:
if not is_valid_email(old_email):
# In previous versions, if 'email' is not in mailbox format,
# we combine 'email' and 'provider' to mailbox format.
old_email = '%s@%s' % (str(old_email), PROVIDER_DOMAIN)
try:
old_user = User.objects.get_old_user(old_email, PROVIDER_DOMAIN, uid)
email = old_user.username
is_new_user = False
except User.DoesNotExist:
email = None
is_new_user = True
else:
email = None
is_new_user = True
try:
user = auth.authenticate(remote_user=email)
@ -193,10 +201,12 @@ def oauth_callback(request):
logger.error(e)
return render_error(request, _('Error, please contact administrator.'))
if not user or not user.is_active:
logger.error('User %s not found or inactive.' % email)
# a page for authenticate user failed
return render_error(request, _('User %s not found.') % email)
if not user:
return render_error(request, _('Error, new user registration is not allowed, please contact administrator.'))
email = user.username
if is_new_user:
SocialAuthUser.objects.add(email, PROVIDER_DOMAIN, uid)
# User is valid. Set request.user and persist user in the session
# by logging the user in.
@ -204,8 +214,8 @@ def oauth_callback(request):
auth.login(request, user)
# update user's profile
name = user_info['name'] if 'name' in user_info else ''
contact_email = user_info['contact_email'] if 'contact_email' in user_info else ''
name = oauth_user_info.get('name', '')
contact_email = oauth_user_info.get('contact_email', '')
profile = Profile.objects.get_profile_by_user(email)
if not profile:
@ -220,12 +230,12 @@ def oauth_callback(request):
profile.save()
if CUSTOM_GET_USER_ROLE:
remote_role_value = user_info.get('role', '')
remote_role_value = oauth_user_info.get('role', '')
if remote_role_value:
role = custom_get_user_role(remote_role_value)
# update user role
ccnet_api.update_role_emailuser(user_info['email'], role)
ccnet_api.update_role_emailuser(email, role)
# update user role quota
role_quota = get_enabled_role_permissions_by_role(role)['role_quota']

View File

@ -17,6 +17,7 @@ from seahub.base.accounts import User
from seahub.settings import INIT_PASSWD, SEND_EMAIL_ON_RESETTING_USER_PASSWD
from seahub.utils import IS_EMAIL_CONFIGURED
from seahub.views.sysadmin import send_user_reset_email
from seahub.profile.models import Profile
from seahub.organizations.views import org_user_exists
@ -62,8 +63,13 @@ class OrgAdminUserSetPassword(APIView):
# send password reset email
if IS_EMAIL_CONFIGURED:
if SEND_EMAIL_ON_RESETTING_USER_PASSWD:
send_to = user.username
profile = Profile.objects.get_profile_by_user(user.username)
if profile and profile.contact_email:
send_to = profile.contact_email
try:
send_user_reset_email(request, user.email, new_password)
send_user_reset_email(request, send_to, new_password)
except Exception as e:
logger.error(str(e))

View File

@ -16,6 +16,7 @@ from seahub.api2.throttling import UserRateThrottle
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.utils import api_error, to_python_boolean
from seahub.api2.endpoints.utils import is_org_user
from seahub.auth.utils import get_virtual_id_by_email
from seahub.base.accounts import User
from seahub.base.models import UserLastLogin
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
@ -260,6 +261,10 @@ class OrgAdminUsers(APIView):
except User.DoesNotExist:
pass
if Profile.objects.filter(contact_email=email).first():
error_msg = _('User %s already exists.') % email
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
user = User.objects.create_user(email, password, is_staff=False,
is_active=True)
@ -682,17 +687,18 @@ class OrgAdminImportUsers(APIView):
})
continue
if not record[1] or not record[1].strip():
if not record[1] or not str(record[1]).strip():
result['failed'].append({
'email': email,
'error_msg': 'password invalid.'
})
continue
else:
password = record[1].strip()
password = str(record[1]).strip()
vid = get_virtual_id_by_email(email)
try:
User.objects.get(email=email)
User.objects.get(email=vid)
result['failed'].append({
'email': email,
'error_msg': 'user %s exists.' % email
@ -701,8 +707,8 @@ class OrgAdminImportUsers(APIView):
except User.DoesNotExist:
pass
User.objects.create_user(email, password, is_staff=False, is_active=True)
set_org_user(org_id, email)
user = User.objects.create_user(email, password, is_staff=False, is_active=True)
set_org_user(org_id, user.email)
if IS_EMAIL_CONFIGURED:
if SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER:
@ -717,7 +723,7 @@ class OrgAdminImportUsers(APIView):
try:
nickname = record[2].strip()
if len(nickname) <= 64 and '/' not in nickname:
Profile.objects.add_or_update(email, nickname, '')
Profile.objects.add_or_update(user.email, nickname, '')
except Exception as e:
logger.error(e)
@ -727,23 +733,21 @@ class OrgAdminImportUsers(APIView):
space_quota_mb = int(record[3])
if space_quota_mb >= 0:
space_quota = int(space_quota_mb) * get_file_size_unit('MB')
seafile_api.set_org_user_quota(org_id, email, space_quota)
seafile_api.set_org_user_quota(org_id, user.email, space_quota)
except Exception as e:
logger.error(e)
user = User.objects.get(email=email)
info = {}
info['email'] = email
info['name'] = email2nickname(email)
info['contact_email'] = email2contact_email(email)
info['email'] = user.email
info['name'] = email2nickname(user.email)
info['contact_email'] = email2contact_email(user.email)
info['is_staff'] = user.is_staff
info['is_active'] = user.is_active
info['quota_usage'] = 0
try:
info['quota_total'] = get_org_user_quota(org_id, email)
info['quota_total'] = get_org_user_quota(org_id, user.email)
except SearpcError as e:
logger.error(e)
info['quota_total'] = -1

View File

@ -316,6 +316,21 @@ ENABLE_WEIXIN = False
# enable dingtalk
ENABLE_DINGTALK = False
# enable ldap
ENABLE_LDAP = False
LDAP_USER_FIRST_NAME_ATTR = ''
LDAP_USER_LAST_NAME_ATTR = ''
LDAP_USER_NAME_REVERSE = False
LDAP_FILTER = ''
LDAP_CONTACT_EMAIL_ATTR = ''
LDAP_USER_ROLE_ATTR = ''
ACTIVATE_USER_WHEN_IMPORT = True
# enable ldap sasl auth
ENABLE_SASL = False
SASL_MECHANISM = ''
SASL_AUTHC_ID_ATTR = ''
# allow user to clean library trash
ENABLE_USER_CLEAN_TRASH = True
@ -974,6 +989,9 @@ if ENABLE_ADFS_LOGIN or ENABLE_MULTI_ADFS:
AUTHENTICATION_BACKENDS += ('seahub.adfs_auth.backends.Saml2Backend',)
SAML_CONFIG_LOADER = 'seahub.adfs_auth.utils.config_settings_loader'
if ENABLE_LDAP:
AUTHENTICATION_BACKENDS += ('seahub.base.accounts.CustomLDAPBackend',)
#####################
# Custom Nav Items #
#####################

View File

@ -185,11 +185,13 @@ class Fixtures(Exam):
if not email:
email = uuid4().hex + '@test.com'
kwargs.setdefault('email', email)
kwargs.setdefault('is_staff', False)
kwargs.setdefault('is_active', True)
user = User(email=email)
user.is_staff = kwargs.get('is_staff', False)
user.is_active = kwargs.get('is_active', True)
user.set_password('secret')
user.save()
return User.objects.create_user(password='secret', **kwargs)
return User.objects.get(email)
def remove_user(self, email=None):
if not email:
@ -280,7 +282,11 @@ class Fixtures(Exam):
return new_org
quota = int(quota)
User.objects.create_user(username, password, is_staff=False, is_active=True)
user = User(email=username)
user.is_staff = False
user.is_active = True
user.set_password(password)
user.save()
create_org(org_name, prefix, username)
new_org = ccnet_threaded_rpc.get_org_by_url_prefix(prefix)
from seahub.organizations.models import OrgMemberQuota
@ -295,7 +301,12 @@ class Fixtures(Exam):
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = User.objects.create_user(email, password, is_staff=False, is_active=True)
user = User(email=email)
user.is_staff = False
user.is_active = True
user.set_password(password)
user.save()
user = User.objects.get(email=email)
ccnet_api.add_org_user(self.org.org_id, email, 0)
return user
@ -305,7 +316,12 @@ class Fixtures(Exam):
try:
admin = User.objects.get(email=email)
except User.DoesNotExist:
admin = User.objects.create_user(email, password, is_staff=False, is_active=True)
user = User(email=email)
user.is_staff = False
user.is_active = True
user.set_password(password)
user.save()
admin = User.objects.get(email=email)
ccnet_api.add_org_user(self.org.org_id, email, 1)
return admin

View File

@ -608,17 +608,17 @@ def user_add(request):
operation=USER_ADD, detail=admin_op_detail)
if user:
User.objects.update_role(email, role)
User.objects.update_role(user.email, role)
if config.FORCE_PASSWORD_CHANGE:
UserOptions.objects.set_force_passwd_change(email)
UserOptions.objects.set_force_passwd_change(user.email)
if name:
Profile.objects.add_or_update(email, name, '')
Profile.objects.add_or_update(user.email, name, '')
if department:
DetailedProfile.objects.add_or_update(email, department, '')
DetailedProfile.objects.add_or_update(user.email, department, '')
if request.user.org:
org_id = request.user.org.org_id
ccnet_threaded_rpc.add_org_user(org_id, email, 0)
ccnet_threaded_rpc.add_org_user(org_id, user.email, 0)
if IS_EMAIL_CONFIGURED:
try:
send_user_add_mail(request, email, password)

View File

@ -17,7 +17,6 @@ from seahub.base.accounts import User
from seahub.avatar.models import Avatar
from seahub.profile.models import Profile
from seahub.utils import render_error, get_site_scheme_and_netloc
from seahub.utils.auth import gen_user_virtual_id
from seahub.auth.models import SocialAuthUser
from seahub.weixin.settings import ENABLE_WEIXIN, \
@ -82,12 +81,14 @@ def weixin_oauth_callback(request):
auth_user = SocialAuthUser.objects.get_by_provider_and_uid('weixin', openid)
if auth_user:
email = auth_user.username
is_new_user = False
else:
email = gen_user_virtual_id()
SocialAuthUser.objects.add(email, 'weixin', openid)
email = None
is_new_user = True
try:
user = auth.authenticate(remote_user=email)
email = user.username
except User.DoesNotExist:
user = None
except Exception as e:
@ -97,6 +98,9 @@ def weixin_oauth_callback(request):
if not user or not user.is_active:
return render_error(request, _('User %s not found or inactive.') % email)
if is_new_user:
SocialAuthUser.objects.add(email, 'weixin', openid)
request.user = user
auth.login(request, user)

View File

@ -22,7 +22,7 @@ from seahub.work_weixin.settings import WORK_WEIXIN_AUTHORIZATION_URL, WORK_WEIX
WORK_WEIXIN_USER_INFO_AUTO_UPDATE, REMEMBER_ME
from seahub.work_weixin.utils import work_weixin_oauth_check, get_work_weixin_access_token, \
handler_work_weixin_api_response, update_work_weixin_user_info
from seahub.utils.auth import gen_user_virtual_id, VIRTUAL_ID_EMAIL_DOMAIN
from seahub.utils.auth import VIRTUAL_ID_EMAIL_DOMAIN
from seahub.auth.models import SocialAuthUser
from django.urls import reverse
@ -94,7 +94,7 @@ def work_weixin_oauth_callback(request):
email = work_weixin_user.username
is_new_user = False
else:
email = gen_user_virtual_id()
email = None
is_new_user = True
try:

View File

@ -11,6 +11,7 @@ from tests.common.common import USERNAME, PASSWORD, \
from tests.common.utils import apiurl, urljoin, randstring
from tests.api.urls import TOKEN_URL, GROUPS_URL, ACCOUNTS_URL, REPOS_URL
from seahub.base.accounts import User
class ApiTestBase(unittest.TestCase):
_token = None
@ -175,8 +176,11 @@ class ApiTestBase(unittest.TestCase):
def create_user(self):
username = '%s@test.com' % randstring(20)
password = randstring(20)
data = {'password': password}
self.admin_put(urljoin(ACCOUNTS_URL, username), data=data, expected=201)
user = User(email=username)
user.is_staff = False
user.is_active = True
user.set_password(password)
user.save()
return _User(username, password)
def remove_user(self, username):

View File

@ -1,276 +0,0 @@
import json
from django.urls import reverse
import seaserv
from seaserv import seafile_api
from seahub.base.accounts import User
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.profile.models import Profile
from seahub.test_utils import BaseTestCase
from tests.common.utils import randstring
class AccountTest(BaseTestCase):
def setUp(self):
self.clear_cache()
self.user1 = self.create_user('user_%s@test.com' % randstring(4),
is_staff=False)
self.user2 = self.create_user('user_%s@test.com' % randstring(4),
is_staff=False)
def tearDown(self):
self.remove_user(self.user1.username)
self.remove_user(self.user2.username)
def _do_create(self):
resp = self.client.put(
reverse('api2-account', args=['new_user_put_create@test.com']),
'password=123456&is_staff=1&is_active=1',
'application/x-www-form-urlencoded',
)
# manually remove this account
self.remove_user(email='new_user_put_create@test.com')
return resp
def _do_get_info(self):
return self.client.get(reverse('api2-account', args=[self.user1.email]))
def _do_migrate(self):
return self.client.post(
reverse('api2-account', args=[self.user1.username]), {
'op': 'migrate',
'to_user': self.user2.username,
}
)
def _do_update(self):
return self.client.put(
reverse('api2-account', args=[self.user1.username]),
'password=654321&is_staff=1&is_active=0&name=user1&storage=102400&login_id=hello',
'application/x-www-form-urlencoded',
)
def _do_update_name(self):
return self.client.put(
reverse('api2-account', args=[self.user1.username]),
'name=user1',
'application/x-www-form-urlencoded',
)
def _do_update_loginid(self):
return self.client.put(
reverse('api2-account', args=[self.user1.username]),
'login_id=hello',
'application/x-www-form-urlencoded',
)
def _do_update_loginid_useemptystring(self):
return self.client.put(
reverse('api2-account', args=[self.user1.username]),
'login_id=',
'application/x-www-form-urlencoded',
)
def _do_update_loginid_sendagain(self):
self.client.put(
reverse('api2-account', args=[self.user1.username]),
'login_id=test',
'application/x-www-form-urlencoded',
)
return self.client.put(
reverse('api2-account', args=[self.user1.username]),
'login_id=test',
'application/x-www-form-urlencoded',
)
def _do_delete(self):
return self.client.delete(
reverse('api2-account', args=[self.user1.username])
)
def test_permission_error(self):
self.login_as(self.user)
resp = self._do_create()
self.assertEqual(403, resp.status_code)
resp = self._do_get_info()
self.assertEqual(403, resp.status_code)
resp = self._do_update()
self.assertEqual(403, resp.status_code)
resp = self._do_migrate()
self.assertEqual(403, resp.status_code)
resp = self._do_delete()
self.assertEqual(403, resp.status_code)
def test_get_info(self):
self.login_as(self.admin)
resp = self._do_get_info()
json_resp = json.loads(resp.content)
assert len(json_resp) == 12
assert json_resp['email'] == self.user1.username
assert json_resp['is_staff'] is False
assert json_resp['is_active'] is True
assert json_resp['usage'] == 0
def test_create(self):
self.login_as(self.admin)
resp = self._do_create()
self.assertEqual(201, resp.status_code)
def test_update(self):
self.login_as(self.admin)
resp = self._do_update()
self.assertEqual(200, resp.status_code)
self.assertTrue(User.objects.get(self.user1.username).check_password(
'654321'))
self.assertTrue(User.objects.get(self.user1.username).is_staff)
self.assertFalse(User.objects.get(self.user1.username).is_active)
self.assertEqual(Profile.objects.get_profile_by_user(
self.user1.username).login_id, 'hello')
self.assertEqual(Profile.objects.get_profile_by_user(
self.user1.username).nickname, 'user1')
self.assertEqual(seafile_api.get_user_quota(
self.user1.username), 102400000000)
def test_update_name(self):
"""only test name"""
self.login_as(self.admin)
resp = self._do_update_name()
self.assertEqual(Profile.objects.get_profile_by_user(
self.user1.username).nickname, 'user1')
def test_update_loginid(self):
"""only test loginid"""
self.login_as(self.admin)
resp = self._do_update_loginid()
self.assertEqual(Profile.objects.get_profile_by_user(
self.user1.username).login_id, 'hello')
def test_update_loginid_useemptystring(self):
"""test loginid, longid send the empty"""
self.login_as(self.admin)
resp = self._do_update_loginid_useemptystring()
self.assertEqual(400, resp.status_code)
def test_update_loginid_sendagain(self):
"""test loginid,sent twice"""
self.login_as(self.admin)
resp = self._do_update_loginid_sendagain()
self.assertEqual(400, resp.status_code)
def test_refresh_profile_cache_after_update(self):
self.login_as(self.admin)
self.assertEqual(email2nickname(self.user1.username),
self.user1.username.split('@')[0])
resp = self._do_update()
self.assertEqual(200, resp.status_code)
self.assertEqual(email2nickname(self.user1.username), 'user1')
def test_migrate(self):
self.login_as(self.admin)
# user1 created a repo
user1_repo = self.create_repo(name='user1-repo', desc='',
username=self.user1.username,
passwd=None)
user1_repos = seafile_api.get_owned_repo_list(self.user1.username)
self.assertEqual(len(user1_repos), 1)
self.assertEqual(user1_repos[0].id, user1_repo)
# user1 created a group and joined a group created by the other
user1_group = self.create_group(group_name='test_group',
username=self.user1.username)
other_group = self.create_group(group_name='other_group',
username=self.user.username)
seaserv.ccnet_threaded_rpc.group_add_member(other_group.id,
self.user.username,
self.user1.username)
user1_groups = seaserv.get_personal_groups_by_user(self.user1.username)
self.assertEqual(len(user1_groups), 2)
real_creator = sorted([self.user1.username, self.user.username])
test_creator = sorted([x.creator_name for x in user1_groups])
self.assertEqual(real_creator, test_creator)
real_id = sorted([user1_group.id, other_group.id])
test_id = sorted([x.id for x in user1_groups])
self.assertEqual(real_id, test_id)
# user2 had no repos
user2_repos = seafile_api.get_owned_repo_list(self.user2.username)
self.assertEqual(len(user2_repos), 0)
# user2 had no groups
user2_groups = seaserv.get_personal_groups_by_user(self.user2.username)
self.assertEqual(len(user2_groups), 0)
# admin migrate account user1 to account user2
resp = self._do_migrate()
self.assertEqual(200, resp.status_code)
### Verify ###
# user1 should have no repos
new_user1_repos = seafile_api.get_owned_repo_list(self.user1.username)
self.assertEqual(len(new_user1_repos), 0)
# user1 should still in two groups, except not the creator anymore in
# the first group, but second group should remain the same
user1_groups = seaserv.get_personal_groups_by_user(self.user1.username)
self.assertEqual(len(user1_groups), 2)
real_creator = sorted([self.user1.username, self.user.username])
test_creator = sorted([x.creator_name for x in user1_groups])
self.assertNotEqual(real_creator, test_creator)
real_id = sorted([user1_group.id, other_group.id])
test_id = sorted([x.id for x in user1_groups])
self.assertEqual(real_id, test_id)
# user2 should have the repo used to be user1's
new_user2_repos = seafile_api.get_owned_repo_list(self.user2.username)
self.assertEqual(len(new_user2_repos), 1)
self.assertEqual(new_user2_repos[0].id, user1_repo)
# user2 should be in two groups, and is the creator of first group,
# but second group should remain the same
user2_groups = seaserv.get_personal_groups_by_user(self.user2.username)
self.assertEqual(len(user2_groups), 2)
real_creator = sorted([self.user2.username, self.user.username])
test_creator = sorted([x.creator_name for x in user2_groups])
self.assertEqual(real_creator, test_creator)
real_id = sorted([user1_group.id, other_group.id])
test_id = sorted([x.id for x in user2_groups])
self.assertEqual(real_id, test_id)
def test_delete(self):
self.login_as(self.admin)
resp = self._do_delete()
self.assertEqual(200, resp.status_code)
def test_new_user_get_info_after_edit_profile(self):
new_user = self.create_user("test@new.user", is_staff=True)
self.login_as(new_user)
resp = self.client.post(reverse('edit_profile'), {
'nickname': 'new nickname'
})
resp = self.client.get(reverse('api2-account', args=[new_user.username]))
json_resp = json.loads(resp.content)
assert len(json_resp) == 12
assert json_resp['email'] == new_user.username
assert json_resp['is_staff'] is True
assert json_resp['is_active'] is True
assert json_resp['usage'] == 0
assert json_resp['institution'] == ''
self.remove_user(new_user.username)

View File

@ -72,12 +72,11 @@ class AdminUsersTest(BaseTestCase):
json_resp = json.loads(resp.content)
self.assertEqual(200, resp.status_code)
assert json_resp['email'] == self.tmp_email
assert json_resp['contact_email'] == self.tmp_email
ccnet_email = ccnet_api.get_emailuser(self.tmp_email)
assert ccnet_email.email == self.tmp_email
ccnet_email = ccnet_api.get_emailuser(json_resp['email'])
self.remove_user(self.tmp_email)
self.remove_user(ccnet_email.email)
def test_create_with_invalid_user_permission(self):
self.login_as(self.user)

View File

@ -78,7 +78,7 @@ class NotificationTest(BaseTestCase):
def test_permission_check_permission_denied(self):
self.login_as(self.user)
new_user = UserManager().create_user(email='new@new.com', password='root')
new_user = self.create_user(email='new@new.com', password='root')
notice_to_new_user = UserNotification.objects.add_user_message(new_user.name, 'test for new user')
data = 'notice_id=%s' % notice_to_new_user.id

View File

@ -1,37 +1,14 @@
import requests
import unittest
from tests.common.utils import apiurl, urljoin, randstring
from tests.common.utils import randstring
from tests.api.apitestbase import ApiTestBase
from tests.api.urls import ACCOUNTS_URL, PING_URL, \
AUTH_PING_URL
from tests.api.urls import PING_URL, AUTH_PING_URL
test_account_username = 'test_%s@test.com' % randstring(10)
test_account_password = randstring(20)
test_account_password2 = randstring(20)
test_account_url = urljoin(ACCOUNTS_URL, test_account_username)
class AccountsApiTest(ApiTestBase):
def test_list_accounts(self):
# Normal user can not list accounts
self.get(ACCOUNTS_URL, expected=403)
accounts = self.admin_get(ACCOUNTS_URL).json()
self.assertGreaterEqual(len(accounts), 2)
# TODO: check returned json, test start/limit param
def test_create_delete_account(self):
data = {'password': test_account_password}
# non-admin user can not create new user
self.put(test_account_url, data=data, expected=403)
self.admin_put(test_account_url, data=data, expected=201)
# non-admin user can not delete a user
self.delete(test_account_url, expected=403)
self.admin_delete(test_account_url)
# check the user is really deleted
self.admin_get(test_account_url, expected=404)
def test_update_account_passwd(self):
with self.get_tmp_user() as user:

View File

@ -60,7 +60,7 @@ class RepoOwnerTest(BaseTestCase):
def test_reshare_to_user_after_transfer_repo(self):
tmp_user = 'tmp_user@email.com'
User.objects.create_user(tmp_user)
self.create_user(tmp_user)
# share user's repo to tmp_user with 'rw' permission
seafile_api.share_repo(self.user_repo_id, self.user.username,
@ -161,7 +161,7 @@ class RepoOwnerTest(BaseTestCase):
def test_reshare_to_user_group_after_transfer_repo(self):
tmp_user = 'tmp_user@email.com'
User.objects.create_user(tmp_user)
self.create_user(tmp_user)
# add admin user to group
ccnet_api.group_add_member(self.group_id, self.user_name, self.admin.username)

View File

@ -128,12 +128,3 @@ class AuthTokenSerializerTest(BaseTestCase):
s = AuthTokenSerializer(data=d, context={'request': self.fake_request})
self.assertSuccess(s)
def test_primary_id(self):
d = {
'username': 'another_email@test.com',
'password': self.user_password,
}
s = AuthTokenSerializer(data=d, context={'request': self.fake_request})
self.assertSuccess(s)

View File

@ -88,12 +88,3 @@ class AuthenticationFormTest(BaseTestCase):
form = AuthenticationForm(None, data)
self.assertSuccess(form)
def test_primary_id(self):
data = {
'login': 'another_email@test.com',
'password': self.user_password,
}
form = AuthenticationForm(None, data)
self.assertSuccess(form)

View File

@ -33,7 +33,7 @@ class UserTest(BaseTestCase):
assert len(UserOptions.objects.filter(email=test_email)) == 0
User.objects.create_user(test_email)
self.create_user(test_email)
UserOptions.objects.enable_server_crypto(test_email)
assert len(UserOptions.objects.filter(email=test_email)) == 1

View File

@ -22,8 +22,6 @@ class RegistrationTest(TestCase):
user = RegistrationProfile.objects.create_inactive_user(site=self.site,
send_email=False,
**self.user_info)
self.assertEqual(user.username, 'test@test.com')
self.assertEqual(user.email, 'test@test.com')
self.assertTrue(user.check_password('password'))
self.assertFalse(user.is_active)

View File

@ -21,7 +21,7 @@ class DemoTest(BaseTestCase):
@override_settings(ENABLE_DEMO_USER=True)
def test_demo_user(self):
u = User.objects.create_user(email=settings.CLOUD_DEMO_USER)
u = self.create_user(email=settings.CLOUD_DEMO_USER)
resp = self.client.get(self.url)
self.assertEqual(302, resp.status_code)

View File

@ -1,3 +1,5 @@
import json
from django.urls import reverse
import pytest
pytestmark = pytest.mark.django_db
@ -24,16 +26,16 @@ class UserAddTest(BaseTestCase):
email=self.new_user, option_key=KEY_FORCE_PASSWD_CHANGE)) == 0
resp = self.client.post(
reverse('user_add',), {
reverse('api-v2.1-admin-users',), {
'email': self.new_user,
'password1': '123',
'password2': '123',
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest'
'password': '123',
}
)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert UserOptions.objects.get(
email=self.new_user,
email=json_resp['email'],
option_key=KEY_FORCE_PASSWD_CHANGE).option_val == VAL_FORCE_PASSWD_CHANGE
def test_can_add_when_pwd_change_not_required(self):
@ -43,13 +45,13 @@ class UserAddTest(BaseTestCase):
email=self.new_user, option_key=KEY_FORCE_PASSWD_CHANGE)) == 0
resp = self.client.post(
reverse('user_add',), {
reverse('api-v2.1-admin-users',), {
'email': self.new_user,
'password1': '123',
'password2': '123',
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest'
'password': '123',
}
)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(UserOptions.objects.filter(
email=self.new_user, option_key=KEY_FORCE_PASSWD_CHANGE)) == 0
email=json_resp['email'], option_key=KEY_FORCE_PASSWD_CHANGE)) == 0

View File

@ -12,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
from seahub.base.accounts import User
from seahub.utils import send_html_email
from seahub.profile.models import Profile
SHA1_RE = re.compile('^[a-f0-9]{40}$')
@ -281,8 +282,14 @@ class RegistrationProfile(models.Model):
subject = ''.join(subject.splitlines())
try:
user = User.objects.get(id=self.emailuser_id)
send_to = user.username
profile = Profile.objects.get_profile_by_user(user.username)
if profile and profile.contact_email:
send_to = profile.contact_email
send_html_email(subject, 'registration/activation_email.html',
ctx_dict, None, [user.username])
ctx_dict, None, [send_to])
except User.DoesNotExist:
pass

View File

@ -54,7 +54,7 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend):
if not local_ccnet_users:
if self.create_unknown_user:
user = User.objects.create_user(
user = User.objects.create_shib_user(
email=username, is_active=self.activate_after_creation)
if user and self.activate_after_creation is False:
notify_admins_on_activate_request(user.email)