Пример #1
0
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
Пример #2
0
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
Пример #3
0
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)
Пример #4
0
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
Пример #5
0
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)
Пример #6
0
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
Пример #7
0
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)
Пример #8
0
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(),
    })
Пример #9
0
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)