def get_or_create_invitation_link(org_id): """Invitation link for an org. Users will be redirected to WeChat QR page. """ if not weixin_check(): return None org_id = int(org_id) expires = 3 * 24 * 60 * 60 def get_token_by_org_id(org_id): return cache.get('org_associate_%d' % org_id, None) def set_token_by_org_id(org_id, token): cache.set('org_associate_%d' % org_id, token, expires) def get_org_id_by_token(token): return cache.get('org_associate_%s' % token, -1) def set_org_id_by_token(token, org_id): cache.set('org_associate_%s' % token, org_id, expires) token = get_token_by_org_id(org_id) cached_org_id = get_org_id_by_token(token) if not token or org_id != cached_org_id: token = gen_token(32) set_token_by_org_id(org_id, token) set_org_id_by_token(token, org_id) link = get_site_scheme_and_netloc() + reverse('weixin_oauth_login') \ + '?org_token=' + token return link
def weixin_oauth_connect_callback(request): if not weixin_check(): return render_error(request, _('Feature is not enabled.')) code = request.GET.get('code', None) state = request.GET.get('state', None) weixin_oauth_connect_state = request.session.get( 'weixin_oauth_connect_state', None) weixin_oauth_connect_redirect = request.session.get( 'weixin_oauth_connect_redirect', redirect_to) is_mobile_weixin = request.session.get( 'weixin_oauth_connect_is_mobile_weixin', False) # clear session try: del request.session['weixin_oauth_connect_state'] del request.session['weixin_oauth_connect_redirect'] del request.session['weixin_oauth_connect_is_mobile_weixin'] except Exception as e: logger.warning(e) # get api user info if state != weixin_oauth_connect_state or not code: logger.error('can not get right code or state from weixin request') return render_error(request, _('Error, please contact administrator.')) access_token, openid = get_weixin_access_token_and_openid( code, is_mobile_weixin) if not access_token or not openid: logger.error('can not get weixin access_token or openid') return render_error(request, _('Error, please contact administrator.')) weixin_api_user_info = get_weixin_api_user_info(access_token, openid) if not weixin_api_user_info: return render_error(request, _('Error, please contact administrator.')) # main user_id = weixin_api_user_info.get('unionid') uid = WEIXIN_UID_PREFIX + user_id username = request.user.username weixin_user = SocialAuthUser.objects.get_by_provider_and_uid( WEIXIN_PROVIDER, uid) if weixin_user: logger.error('weixin account already exists %s' % user_id) return render_error(request, '出错了,此微信账号已被绑定') SocialAuthUser.objects.add(username, WEIXIN_PROVIDER, uid) # update user info if WEIXIN_USER_INFO_AUTO_UPDATE: api_user = weixin_api_user_info api_user['username'] = username update_weixin_user_info(api_user) # redirect user to page response = HttpResponseRedirect(weixin_oauth_connect_redirect) return response
def weixin_oauth_connect(request): if not weixin_check(): return render_error(request, _('Feature is not enabled.')) # mobile weixin user_agent = request.META.get('HTTP_USER_AGENT', '').lower() if 'mobile' in user_agent and 'micromessenger' not in user_agent: return render_error(request, '请在微信客户端打开链接') if 'micromessenger' in user_agent: if not mp_weixin_check(): return render_error(request, '微信客户端登录功能未启用,请联系管理员') is_mobile_weixin = True scope = 'snsapi_userinfo' weixin_authorization_url = MP_WEIXIN_AUTHORIZATION_URL appid = MP_WEIXIN_APP_ID else: is_mobile_weixin = False scope = 'snsapi_login' weixin_authorization_url = WEIXIN_AUTHORIZATION_URL appid = WEIXIN_APP_ID state = str(uuid.uuid4()) request.session['weixin_oauth_connect_state'] = state request.session['weixin_oauth_connect_redirect'] = request.GET.get( auth.REDIRECT_FIELD_NAME, redirect_to) request.session['weixin_oauth_connect_is_mobile_weixin'] = is_mobile_weixin response_type = 'code' # from weixn official document wechat_redirect = '#wechat_redirect' data = { 'appid': appid, 'redirect_uri': get_site_scheme_and_netloc() + reverse('weixin_oauth_connect_callback'), 'response_type': response_type, 'scope': scope, 'state': state, } authorization_url = weixin_authorization_url + '?' + urllib.parse.urlencode( data) + wechat_redirect return HttpResponseRedirect(authorization_url)
def weixin_oauth_disconnect(request): if not weixin_check(): return render_error(request, _('Feature is not enabled.')) username = request.user.username if username[-(len(VIRTUAL_ID_EMAIL_DOMAIN)):] == VIRTUAL_ID_EMAIL_DOMAIN: profile = Profile.objects.get_profile_by_user(username) if not profile or not (profile.contact_email or profile.phone): return render_error(request, '出错了,当前账号不能解绑微信,请绑定手机号或邮箱后再试') SocialAuthUser.objects.delete_by_username_and_provider( username, WEIXIN_PROVIDER) # redirect user to page response = HttpResponseRedirect( request.GET.get(auth.REDIRECT_FIELD_NAME, redirect_to)) return response
def weixin_oauth_login(request): if not weixin_check(): return render_error(request, _('Feature is not enabled.')) # org invite for new user org_token = request.GET.get('org_token', None) org_id = None if org_token: # validate token org_id = cache.get('org_associate_%s' % org_token, -1) if org_id <= 0: return render_error(request, '邀请链接无效') # get org info org = ccnet_api.get_org_by_id(org_id) if not org: return render_error(request, '机构不存在') # check org member quota if ORG_MEMBER_QUOTA_ENABLED: from seahub.organizations.models import OrgMemberQuota org_members = len( ccnet_api.get_org_users_by_url_prefix(org.url_prefix, -1, -1)) org_members_quota = OrgMemberQuota.objects.get_quota(org_id) if org_members_quota is not None and org_members >= org_members_quota: return render_error(request, _("Out of quota.")) # already authenticated if request.user.is_authenticated(): if org_id: return render_error(request, '仅限新用户加入机构') else: return HttpResponseRedirect( request.GET.get(auth.REDIRECT_FIELD_NAME, redirect_to)) # mobile weixin user_agent = request.META.get('HTTP_USER_AGENT', '').lower() if 'mobile' in user_agent and 'micromessenger' not in user_agent: return render(request, 'redirect_mobile_weixin.html') if 'micromessenger' in user_agent: if not mp_weixin_check(): return render_error(request, '微信客户端登录功能未启用,请联系管理员') is_mobile_weixin = True scope = 'snsapi_userinfo' weixin_authorization_url = MP_WEIXIN_AUTHORIZATION_URL appid = MP_WEIXIN_APP_ID else: is_mobile_weixin = False scope = 'snsapi_login' weixin_authorization_url = WEIXIN_AUTHORIZATION_URL appid = WEIXIN_APP_ID # main state = str(uuid.uuid4()) request.session['weixin_oauth_state'] = state request.session['weixin_oauth_redirect'] = request.GET.get( auth.REDIRECT_FIELD_NAME, redirect_to) request.session['weixin_oauth_org_id'] = org_id request.session['weixin_oauth_is_mobile_weixin'] = is_mobile_weixin response_type = 'code' # from weixn official document wechat_redirect = '#wechat_redirect' data = { 'appid': appid, 'redirect_uri': get_site_scheme_and_netloc() + reverse('weixin_oauth_callback'), 'response_type': response_type, 'scope': scope, 'state': state, } authorization_url = weixin_authorization_url + '?' + urllib.parse.urlencode( data) + wechat_redirect return HttpResponseRedirect(authorization_url)
def weixin_oauth_callback(request): if not weixin_check(): return render_error(request, _('Feature is not enabled.')) code = request.GET.get('code', None) state = request.GET.get('state', None) weixin_oauth_state = request.session.get('weixin_oauth_state', None) weixin_oauth_redirect = request.session.get('weixin_oauth_redirect', redirect_to) org_id = request.session.get('weixin_oauth_org_id', None) is_mobile_weixin = request.session.get('weixin_oauth_is_mobile_weixin', False) # clear session try: del request.session['weixin_oauth_state'] del request.session['weixin_oauth_redirect'] del request.session['weixin_oauth_org_id'] del request.session['weixin_oauth_is_mobile_weixin'] except Exception as e: logger.warning(e) # get api user info if state != weixin_oauth_state or not code: logger.error('can not get right code or state from weixin request') return render_error(request, _('Error, please contact administrator.')) access_token, openid = get_weixin_access_token_and_openid( code, is_mobile_weixin) if not access_token or not openid: logger.error('can not get weixin access_token or openid') return render_error(request, _('Error, please contact administrator.')) weixin_api_user_info = get_weixin_api_user_info(access_token, openid) if not weixin_api_user_info: return render_error(request, _('Error, please contact administrator.')) # main user_id = weixin_api_user_info.get('unionid') uid = WEIXIN_UID_PREFIX + user_id weixin_user = SocialAuthUser.objects.get_by_provider_and_uid( WEIXIN_PROVIDER, uid) if weixin_user: email = weixin_user.username is_new_user = False else: email = None is_new_user = True try: user = auth.authenticate(remote_user=email) except User.DoesNotExist: user = None if not user: return render_error( request, _('Error, new user registration is not allowed, please contact administrator.' )) # bind username = user.username if is_new_user: SocialAuthUser.objects.add(username, WEIXIN_PROVIDER, uid) # org invite for new user if org_id: if is_new_user: ccnet_api.add_org_user(org_id, username, int(False)) else: return render_error(request, '仅限新用户加入机构') # update user info if is_new_user or WEIXIN_USER_INFO_AUTO_UPDATE: api_user = weixin_api_user_info api_user['username'] = username update_weixin_user_info(api_user) if not user.is_active: return render_error( request, _('Your account is created successfully, please wait for administrator to activate your account.' )) # User is valid. Set request.user and persist user in the session # by logging the user in. request.user = user request.session['remember_me'] = REMEMBER_ME auth.login(request, user) # generate auth token for Seafile client api_token = get_api_token(request) # redirect user to page response = HttpResponseRedirect(weixin_oauth_redirect) response.set_cookie('seahub_auth', user.username + '@' + api_token.key) return response
def register(request, backend, success_url=None, form_class=None, disallowed_url='registration_disallowed', template_name='registration/registration_form.html', extra_context=None, redirect_field_name=REDIRECT_FIELD_NAME): """ Allow a new user to register an account. The actual registration of the account will be delegated to the backend specified by the ``backend`` keyword argument (see below); it will be used as follows: 1. The backend's ``registration_allowed()`` method will be called, passing the ``HttpRequest``, to determine whether registration of an account is to be allowed; if not, a redirect is issued to the view corresponding to the named URL pattern ``registration_disallowed``. To override this, see the list of optional arguments for this view (below). 2. The form to use for account registration will be obtained by calling the backend's ``get_form_class()`` method, passing the ``HttpRequest``. To override this, see the list of optional arguments for this view (below). 3. If valid, the form's ``cleaned_data`` will be passed (as keyword arguments, and along with the ``HttpRequest``) to the backend's ``register()`` method, which should return the new ``User`` object. 4. Upon successful registration, the backend's ``post_registration_redirect()`` method will be called, passing the ``HttpRequest`` and the new ``User``, to determine the URL to redirect the user to. To override this, see the list of optional arguments for this view (below). **Required arguments** None. **Optional arguments** ``backend`` The dotted Python import path to the backend class to use. ``disallowed_url`` URL to redirect to if registration is not permitted for the current ``HttpRequest``. Must be a value which can legally be passed to ``django.shortcuts.redirect``. If not supplied, this will be whatever URL corresponds to the named URL pattern ``registration_disallowed``. ``form_class`` The form class to use for registration. If not supplied, this will be retrieved from the registration backend. ``extra_context`` A dictionary of variables to add to the template context. Any callable object in this dictionary will be called to produce the end result which appears in the context. ``success_url`` URL to redirect to after successful registration. Must be a value which can legally be passed to ``django.shortcuts.redirect``. If not supplied, this will be retrieved from the registration backend. ``template_name`` A custom template to use. If not supplied, this will default to ``registration/registration_form.html``. **Context:** ``form`` The registration form. Any extra variables supplied in the ``extra_context`` argument (see above). **Template:** registration/registration_form.html or ``template_name`` keyword argument. """ if not settings.ENABLE_SIGNUP: raise Http404 if settings.ACTIVATE_AFTER_REGISTRATION: success_url = settings.SITE_ROOT redirect_to = request.GET.get(redirect_field_name) if redirect_to: success_url = redirect_to backend = get_backend(backend) if not backend.registration_allowed(request): return redirect(disallowed_url) if form_class is None: form_class = backend.get_form_class(request) if request.method == 'POST': form = form_class(data=request.POST, files=request.FILES) if form.is_valid(): new_user = backend.register(request, **form.cleaned_data) if success_url is None: to, args, kwargs = backend.post_registration_redirect( request, new_user) return redirect(to, *args, **kwargs) else: return redirect(success_url) else: userid = request.GET.get('userid', '') form = form_class(initial={'userid': userid}) if extra_context is None: extra_context = {} context = {} for key, value in list(extra_context.items()): context[key] = callable(value) and value() or value src = request.GET.get('src', '') if src: form = form_class(initial={'email': src}) context['form'] = form context['min_len'] = config.USER_PASSWORD_MIN_LENGTH context['strong_pwd_required'] = config.USER_STRONG_PASSWORD_REQUIRED context['level'] = config.USER_PASSWORD_STRENGTH_LEVEL login_bg_image_path = get_login_bg_image_path() context['login_bg_image_path'] = login_bg_image_path context['enable_weixin'] = weixin_check() context['redirect_to'] = redirect_to or reverse('dtable') return render(request, template_name, context)
def login(request, template_name='registration/login.html', redirect_if_logged_in='dtable', redirect_field_name=REDIRECT_FIELD_NAME, authentication_form=AuthenticationForm): """Displays the login form and handles the login action.""" redirect_to = request.GET.get(redirect_field_name, '') if request.user.is_authenticated(): if redirect_to: return HttpResponseRedirect(redirect_to) else: return HttpResponseRedirect(reverse('dtable')) ip = get_remote_ip(request) if request.method == "POST": login = request.POST.get('login', '').strip() failed_attempt = get_login_failed_attempts(username=login, ip=ip) remember_me = True if request.POST.get('remember_me', '') == 'on' else False redirect_to = request.POST.get(redirect_field_name, '') or redirect_to # check the form used_captcha_already = False if bool(config.FREEZE_USER_ON_LOGIN_FAILED) is True: form = authentication_form(data=request.POST) else: if failed_attempt >= config.LOGIN_ATTEMPT_LIMIT: form = CaptchaAuthenticationForm(data=request.POST) used_captcha_already = True else: form = authentication_form(data=request.POST) if form.is_valid(): return _handle_login_form_valid(request, form.get_user(), redirect_to, remember_me) # form is invalid user_logged_in_failed.send(sender=None, request=request) failed_attempt = incr_login_failed_attempts(username=login, ip=ip) if failed_attempt >= config.LOGIN_ATTEMPT_LIMIT: if bool(config.FREEZE_USER_ON_LOGIN_FAILED) is True: # log user in if password is valid otherwise freeze account logger.warn('Login attempt limit reached, try freeze the user, email/username: %s, ip: %s, attemps: %d' % (login, ip, failed_attempt)) email = Profile.objects.get_username_by_login_id(login) if email is None: email = login try: user = User.objects.get(email) if user.is_active: user.freeze_user(notify_admins=True) logger.warn('Login attempt limit reached, freeze the user email/username: %s, ip: %s, attemps: %d' % (login, ip, failed_attempt)) except User.DoesNotExist: logger.warn('Login attempt limit reached with invalid email/username: %s, ip: %s, attemps: %d' % (login, ip, failed_attempt)) pass form.errors['freeze_account'] = _('This account has been frozen due to too many failed login attempts.') else: # use a new form with Captcha logger.warn('Login attempt limit reached, show Captcha, email/username: %s, ip: %s, attemps: %d' % (login, ip, failed_attempt)) if not used_captcha_already: form = CaptchaAuthenticationForm() else: ### GET failed_attempt = get_login_failed_attempts(ip=ip) if failed_attempt >= config.LOGIN_ATTEMPT_LIMIT: if bool(config.FREEZE_USER_ON_LOGIN_FAILED) is True: form = authentication_form() else: logger.warn('Login attempt limit reached, show Captcha, ip: %s, attempts: %d' % (ip, failed_attempt)) form = CaptchaAuthenticationForm() else: form = authentication_form() request.session.set_test_cookie() current_site = get_current_site(request) multi_tenancy = getattr(settings, 'MULTI_TENANCY', False) if config.ENABLE_SIGNUP: if multi_tenancy: org_account_only = getattr(settings, 'FORCE_ORG_REGISTER', False) if org_account_only: signup_url = reverse('org_register') else: signup_url = reverse('choose_register') else: signup_url = reverse('registration_register') else: signup_url = '' enable_sso = getattr(settings, 'ENABLE_SHIB_LOGIN', False) or \ getattr(settings, 'ENABLE_KRB5_LOGIN', False) or \ getattr(settings, 'ENABLE_ADFS_LOGIN', False) or \ getattr(settings, 'ENABLE_SAML', False) or \ getattr(settings, 'ENABLE_OAUTH', False) or \ getattr(settings, 'ENABLE_CAS', False) or \ getattr(settings, 'ENABLE_REMOTE_USER_AUTHENTICATION', False) or \ getattr(settings, 'ENABLE_WORK_WEIXIN', False) login_bg_image_path = get_login_bg_image_path() return render(request, template_name, { 'form': form, redirect_field_name: redirect_to, 'site': current_site, 'site_name': get_site_name(), 'remember_days': config.LOGIN_REMEMBER_DAYS, 'signup_url': signup_url, 'enable_sso': enable_sso, 'login_bg_image_path': login_bg_image_path, 'enable_weixin': weixin_check(), })
def edit_profile(request): """ Show and edit user profile. """ username = request.user.username form_class = ProfileForm if request.method == 'POST': form = form_class(user=request.user, data=request.POST) if form.is_valid(): form.save() messages.success(request, _('Successfully edited profile.')) return HttpResponseRedirect(reverse('edit_profile')) else: messages.error(request, _('Failed to edit profile')) else: profile = Profile.objects.get_profile_by_user(username) init_dict = {} if profile: init_dict['nickname'] = profile.nickname init_dict['login_id'] = profile.login_id init_dict['contact_email'] = profile.contact_email init_dict['list_in_address_book'] = profile.list_in_address_book form = form_class(user=request.user, data=init_dict) # common logic try: server_crypto = UserOptions.objects.is_server_crypto(username) except CryptoOptionNotSetError: # Assume server_crypto is ``False`` if this option is not set. server_crypto = False sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) default_repo_id = UserOptions.objects.get_default_repo(username) if default_repo_id: default_repo = seafile_api.get_repo(default_repo_id) else: default_repo = None owned_repos = [] if settings.ENABLE_WEBDAV_SECRET: decoded = UserOptions.objects.get_webdav_decoded_secret(username) webdav_passwd = decoded if decoded else '' else: webdav_passwd = '' email_inverval = UserOptions.objects.get_dtable_updates_email_interval( username) email_inverval = email_inverval if email_inverval is not None else 0 # social oauth enable_work_wixin = False enable_wixin = False work_wixin_connected = False wixin_connected = False if work_weixin_oauth_check(): enable_work_wixin = True work_wixin_connected = SocialAuthUser.objects.filter( username=request.user.username, provider=WORK_WEIXIN_PROVIDER).exists() if weixin_check(): enable_wixin = True wixin_connected = SocialAuthUser.objects.filter( username=request.user.username, provider=WEIXIN_PROVIDER).exists() resp_dict = { 'form': form, 'server_crypto': server_crypto, "sub_lib_enabled": sub_lib_enabled, 'ENABLE_ADDRESSBOOK_OPT_IN': settings.ENABLE_ADDRESSBOOK_OPT_IN, 'default_repo': default_repo, 'owned_repos': owned_repos, 'is_pro': is_pro_version(), 'is_ldap_user': is_ldap_user(request.user), 'two_factor_auth_enabled': has_two_factor_auth(), 'ENABLE_CHANGE_PASSWORD': settings.ENABLE_CHANGE_PASSWORD, 'ENABLE_WEBDAV_SECRET': settings.ENABLE_WEBDAV_SECRET, 'ENABLE_DELETE_ACCOUNT': ENABLE_DELETE_ACCOUNT, 'ENABLE_UPDATE_USER_INFO': ENABLE_UPDATE_USER_INFO, 'webdav_passwd': webdav_passwd, 'email_notification_interval': email_inverval, 'enable_work_wixin': enable_work_wixin, 'enable_wixin': enable_wixin, 'work_wixin_connected': work_wixin_connected, 'wixin_connected': wixin_connected, 'social_next_page': reverse('edit_profile'), 'ENABLE_USER_SET_CONTACT_EMAIL': settings.ENABLE_USER_SET_CONTACT_EMAIL, 'user_unusable_password': request.user.enc_password == UNUSABLE_PASSWORD, 'enable_bind_phone': ENABLE_BIND_PHONE, } if has_two_factor_auth(): from seahub.two_factor.models import StaticDevice, default_device try: backup_tokens = StaticDevice.objects.get( user=request.user.username).token_set.count() except StaticDevice.DoesNotExist: backup_tokens = 0 resp_dict['default_device'] = default_device(request.user) resp_dict['backup_tokens'] = backup_tokens #template = 'profile/set_profile.html' template = 'profile/set_profile_react.html' return render(request, template, resp_dict)