Example #1
0
def verify():
    form = VerifyForm(request.form)

    if request.method == "POST" and form.validate():
        # verify otp
        if session.get('tfa-logged-in', False) is not True:
            return redirect(url_for('auth.login'))

        user = User.objects(name=session['tfa-user']).next()

        ok, drift = accept_totp(format='dec6',
                                key=binascii.hexlify(base64.b32decode(user.tfa_secret)),
                                response=form.code.data,
                                drift=user.tfa_info.get('drift', 0))

        if not ok:
            form.errors['verify'] = ["Incorrect verification code."]
            return render_template('verify.html', form=form, title="Verify Login")

        if login_user(user, remember=session['tfa-remember']):
            user.tfa_info['drift'] = drift
            user.save()
            flash("Logged in!", category="success")
            return redirect(request.args.get("next", '/'))

    return render_template("verify.html", form=form, title="Verify Login")
Example #2
0
    def authenticate(self, context, auth_payload, auth_context):
        """Try to authenticate against the identity backend."""
        user_info = auth_plugins.UserAuthInfo.create(auth_payload, self.method)

        # Before we do anything else, make sure that this user is allowed
        #  access from their source IP
        password_whitelist.check_whitelist(
            user_info.user_id, context['environment']['REMOTE_ADDR'])

        # FIXME(gyee): identity.authenticate() can use some refactoring since
        # all we care is password matches
        try:
            self.identity_api.authenticate(context,
                                           user_id=user_info.user_id,
                                           password=user_info.password)
        except AssertionError:
            # authentication failed because of invalid username or password
            msg = _('Invalid username or password')
            raise exception.Unauthorized(msg)

        # Password auth succeeded, check two-factor
        # LOG.debug("OATH: Doing 2FA for user_info " +
        #     ( "%s(%r)" % (user_info.__class__, user_info.__dict__) ) )
        # LOG.debug("OATH: Doing 2FA for auth_payload " +
        #     ( "%s(%r)" % (auth_payload.__class__, auth_payload) ) )
        cnx = mysql.connector.connect(user=CONF.oath.dbuser,
                                      password=CONF.oath.dbpass,
                                      database=CONF.oath.dbname,
                                      host=CONF.oath.dbhost)
        cur = cnx.cursor(buffered=True)
        sql = ('SELECT oath.secret as secret from user '
               'left join oathauth_users as oath on oath.id = user.user_id '
               'where user.user_name = %s LIMIT 1')
        cur.execute(sql, (user_info.user_ref['name'], ))
        secret = cur.fetchone()[0]

        if secret:
            if 'totp' in auth_payload['user']:
                (p, d) = oath.accept_totp(base64.b16encode(
                    base64.b32decode(secret)),
                                          auth_payload['user']['totp'],
                                          forward_drift=2,
                                          backward_drift=2)
                if p:
                    LOG.debug("OATH: 2FA passed")
                else:
                    LOG.debug("OATH: 2FA failed")
                    msg = _('Invalid two-factor token')
                    raise exception.Unauthorized(msg)
            else:
                LOG.debug("OATH: 2FA failed, missing totp param")
                msg = _('Missing two-factor token')
                raise exception.Unauthorized(msg)
        else:
            LOG.debug("OATH: user '%s' does not have 2FA enabled.",
                      user_info.user_ref['name'])
            msg = _('2FA is not enabled; login forbidden')
            raise exception.Unauthorized(msg)

        auth_context['user_id'] = user_info.user_id
Example #3
0
 def verify_code(self, code):
     accepted, drift = accept_totp(key=self.totp_secret,
                                   response=code,
                                   drift=self.totp_drift)
     if accepted:
         self.totp_drift = drift
         return True
     return False
Example #4
0
 def clean(self):
     token = self.cleaned_data.get('token')
     if token:
         accepted, drift = accept_totp(key=self.seed, response=token)
         if not accepted:
             raise forms.ValidationError(
                 self.error_messages['invalid_token'])
     return self.cleaned_data
Example #5
0
 def verify_code(self, code):
     accepted, drift = accept_totp(key=self.totp_secret,
                                   response=code,
                                   drift=self.totp_drift)
     if accepted:
         self.totp_drift = drift
         return True
     return False
Example #6
0
 def clean(self):
     token = self.cleaned_data.get('token')
     if token:
         accepted, drift = accept_totp(key=self.seed, response=token)
         if not accepted:
             raise forms.ValidationError(
                 self.error_messages['invalid_token'])
     return self.cleaned_data
Example #7
0
 def test_totp(self):
     for t, _, _, response, algo_key in self.tv:
         algo = self.hash_algos[algo_key]
         self.assertTrue(
             accept_totp(algo['key'],
                         response,
                         t=int(t),
                         hash=algo['alg'],
                         format='dec8'))
Example #8
0
 def verify_google_auth(self, code):
     # FIXME: Read from google auth configurations (and make configuration for google auth!)
     is_valid, drift = oath.accept_totp(key=self.seed,
                                        response=code,
                                        period=30,
                                        format='dec6',
                                        hash='SHA1',
                                        drift=0,
                                        backward_drift=3,
                                        forward_drift=3)
     return is_valid, drift
Example #9
0
def check_raw_seed(raw_seed, auth_code, token_type=None):
    """
    Checks whether `auth_code` is a valid authentication code at the current time,
    based on the `raw_seed` (raw byte string representation of `seed`).
    """
    if not token_type:
        token_type = DEFAULT_TOKEN_TYPE
    return accept_totp(auth_code,
                       hexlify(raw_seed),
                       token_type,
                       period=PERIOD,
                       forward_drift=FORWARD_DRIFT,
                       backward_drift=BACKWARD_DRIFT)[0]
Example #10
0
def check_raw_seed(raw_seed, auth_code, token_type=None):
    """
    Checks whether `auth_code` is a valid authentication code at the current time,
    based on the `raw_seed` (raw byte string representation of `seed`).
    """
    if not token_type:
        token_type = DEFAULT_TOKEN_TYPE
    return accept_totp(
        auth_code,
        hexlify(raw_seed),
        token_type,
        period=PERIOD,
        forward_drift=FORWARD_DRIFT,
        backward_drift=BACKWARD_DRIFT
    )[0]
Example #11
0
def tfa_enable():
    form = TOTPSetupForm(request.form)

    if request.method == "GET":
        # generate a new secret
        secret = ''
        rand = random.SystemRandom()
        for i in range(30):
            secret += chr(rand.getrandbits(8))
        session['tfa-new-method'] = 'TOTP'
        session['tfa-new-secret'] = base64.b32encode(secret)
    elif request.method == "POST" and form.validate():
        method = session.get('tfa-new-method', None)
        if method == 'TOTP':
            # check code
            key = binascii.hexlify(base64.b32decode(session['tfa-new-secret']))
            ok, drift = accept_totp(format='dec6',
                                    key=key,
                                    response=form.code.data)
            if not ok:
                form.errors['tfa'] = ['Verification error, please try again.']
            else:
                current_user.tfa = True
                current_user.tfa_method = 'TOTP'
                current_user.tfa_secret = session['tfa-new-secret']
                current_user.tfa_info['drift'] = drift
                current_user.save()
                del session['tfa-new-method']
                del session['tfa-new-secret']
                flash('Two-Factor Authentication enabled', category='success')
                return redirect(url_for('settings.tfa_pane')), 303
        else:
            abort(401)
    else:
        abort(403)

    text = session['tfa-new-secret']
    readable = ' '.join(text[i:i + 4] for i in range(0, len(text), 4))

    return render_template(
        'settings_tfa_enable.html',
        settings_panels_structure=settings_panels_structure,
        secret=readable,
        form=form,
        title="TFA - Account - Settings",
        totp_url=_totp_url(secret=session['tfa-new-secret']))
Example #12
0
def tfa_enable():
    form = TOTPSetupForm(request.form)

    if request.method == "GET":
        # generate a new secret
        secret = ''
        rand = random.SystemRandom()
        for i in range(30):
            secret += chr(rand.getrandbits(8))
        session['tfa-new-method'] = 'TOTP'
        session['tfa-new-secret'] = base64.b32encode(secret)
    elif request.method == "POST" and form.validate():
        method = session.get('tfa-new-method', None)
        if method == 'TOTP':
            # check code
            key = binascii.hexlify(base64.b32decode(session['tfa-new-secret']))
            ok, drift = accept_totp(format='dec6', key=key, response=form.code.data)
            if not ok:
                form.errors['tfa'] = ['Verification error, please try again.']
            else:
                current_user.tfa = True
                current_user.tfa_method = 'TOTP'
                current_user.tfa_secret = session['tfa-new-secret']
                current_user.tfa_info['drift'] = drift
                current_user.save()
                del session['tfa-new-method']
                del session['tfa-new-secret']
                flash('Two-Factor Authentication enabled', category='success')
                return redirect(url_for('settings.tfa_pane')), 303
        else:
            abort(401)
    else:
        abort(403)

    text = session['tfa-new-secret']
    readable = ' '.join(text[i:i + 4] for i in range(0, len(text), 4))

    return render_template('settings_tfa_enable.html',
                           settings_panels_structure=settings_panels_structure,
                           secret=readable, form=form, title="TFA - Account - Settings",
                           totp_url=_totp_url(secret=session['tfa-new-secret']))
Example #13
0
def check_raw_seed(raw_seed, auth_code, token_type=None):
    """
    Checks whether `auth_code` is a valid authentication code at the current time,
    based on the `raw_seed` (raw byte string representation of `seed`).
    """
    if not token_type:
        token_type = DEFAULT_TOKEN_TYPE
    try:
        result = accept_totp(
            response=auth_code,
            key=hexlify(raw_seed),
            format=token_type,
            period=PERIOD,
            forward_drift=FORWARD_DRIFT,
            backward_drift=BACKWARD_DRIFT
        )
        log.debug(result)
        return result[0]
    except Exception as e:
        log.debug(e)
        log.debug(traceback.format_exc())

    return False
Example #14
0
    def post(self, request):
        """
        登陆信息
        ---
        parameters:
            - name: username
              description: 用户名
              type: string
              paramType: form
              required: true
            - name: password
              description: 密码
              type: string
              paramType: form
              required: true
            - name: gcode
              description: google二次验证码
              type: string
              paramType: form
              required: true
        """
        step = request.DATA.get('step')
        if step not in ['auth', 'token']:
            context = {"status": 500, "msg": "登录信息错误", "error": "登录步骤错误"}
            return Response(context)
        secret_key = getattr(settings, "SECRET_KEY")
        login_salt = getattr(settings, "LOGIN_SALT")
        s = itsdangerous.URLSafeTimedSerializer(secret_key, salt=login_salt)
        agent = self.get_agent()
        try:
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                real_ip = x_forwarded_for.split(',')[0]
            else:
                real_ip = request.META.get('REMOTE_ADDR')
        except Exception as e:
            real_ip = ""
        if agent and self.ip_is_ban(agent, real_ip):
            context = {
                "status": 400,
                "msg": u"IP超出登录失败限制次数,已被锁定",
                "error": u"IP超出登录失败限制次数,已被锁定",
            }
            self.fail_ip_login(agent, real_ip)
            return Response(context)

        verified = False
        if step == "auth":
            # 用户名和密码验证
            username = str(request.DATA.get('username'))
            password = str(request.DATA.get('password'))
            noauth_user = get_user_from_username(username=username,
                                                 agent=agent)
            if not noauth_user:
                if agent:
                    # agent.login_fail_ip(real_ip)
                    self.fail_ip_login(agent, real_ip)
                context = {
                    "status": 400,
                    "msg": u"用户名或密码错误",
                    "error": u"用户名或密码错误"
                }
                return Response(context)
            the_login_status, login_msg = self.user_is_ban(noauth_user)
            if the_login_status:
                context = {
                    "status": 400,
                    "msg": login_msg,
                    "error": login_msg,
                }
                self.fail_user_login(noauth_user)
                if agent:
                    self.fail_ip_login(agent, real_ip)
                return Response(context)

            user = auth.authenticate(username=noauth_user.username,
                                     password=password)

            if user and user.is_active:

                if user.userinfo.is_locked:
                    context = {
                        "status": 400,
                        "msg": u"用户名被锁定,不能登录",
                        "error": u"用户名被锁定,不能登录"
                    }
                    self.fail_user_login(user)
                    if agent:
                        self.fail_ip_login(agent, real_ip)
                    return Response(context)

                if not user.userinfo.agent.login_allow:
                    # 不允许登陆 除了代理商管理员以外都不能登陆
                    role = user.userinfo.role_type
                    if role == 2 and user.userinfo.is_admin:
                        pass
                    elif role == 1:
                        pass
                    else:
                        self.fail_user_login(user)
                        if agent:
                            self.fail_ip_login(agent, real_ip)
                        return Response({
                            "status": 500,
                            "msg": "不允许登陆,请联系机房管理员",
                            "error": "不允许登陆,请联系机房管理员"
                        })

                # 当前账号的角色是否要二次验证,多对多关系,查出所有,若所有角色身份其中任意一个身份要验证,则验证
                roles = user.userinfo.roles.all()
                two_factor = [obj.two_factor for obj in roles]
                if user.userinfo.gs_status == 1 or sum(two_factor):
                    # 开启了二次验证
                    token = s.dumps(user.username)
                    context = {
                        "status": 200,
                        "msg": "请输入二次验证",
                        "data": {
                            "token": token,
                            "step": "token"
                        }
                    }
                    return Response(context)

                else:
                    verified = True
            else:
                # noauth_user.userinfo.login_fail()
                self.fail_user_login(noauth_user)
                if agent:
                    self.fail_ip_login(agent, real_ip)
                    # agent.login_fail_ip(real_ip)
                context = {
                    "status": 400,
                    "msg": u"用户名或密码错误",
                    "error": u"用户名或密码错误"
                }
                return Response(context)
        else:
            # "token"
            g_code = request.DATA.get('g_code')
            token = request.DATA.get('token')
            # 验证 token正确性和有效性,并获得用户
            try:
                username = s.loads(token, max_age=600)
            except itsdangerous.SignatureExpired as e:
                context = {
                    "status": 500,
                    "msg": "登录超时,请重新登录",
                    "error": "timeout",
                    "data": {
                        "error": "timeout",
                        "action": "re_login"
                    }
                }
                if agent:
                    self.fail_ip_login(agent, real_ip)
                return Response(context)
            except Exception as e:
                context = {
                    "status": 500,
                    "msg": "验证错误,请重新登录",
                    "error": "sign_error#1",
                    "data": {
                        "error": "sign_error",
                        "action": "re_login"
                    }
                }
                if agent:
                    self.fail_ip_login(agent, real_ip)
                return Response(context)

            user = User.objects.get(username=username)
            if not user:
                context = {
                    "status": 400,
                    "msg": u"验证错误,请重新登录",
                    "error": u"sign_error#2"
                }
                if agent:
                    self.fail_ip_login(agent, real_ip)
                return Response(context)
            google_secret = user.userinfo.google_secret
            try:
                result = accept_totp(google_secret, g_code, "dec6")
                if not result[0]:
                    context = {
                        "status": 400,
                        "msg": u"验证码错误",
                        "error": u"验证码错误"
                    }
                    self.fail_user_login(user)
                    if agent:
                        self.fail_ip_login(agent, real_ip)
                    return Response(context)
                verified = True
            except TypeError as e:
                context = {"status": 400, "msg": u"验证码错误", "error": u"验证码错误"}
                self.fail_user_login(user)
                if agent:
                    self.fail_ip_login(agent, real_ip)
                return Response(context)

        if verified:
            user.backend = 'django.contrib.auth.backends.ModelBackend'
            auth.login(request, user)

            agent_perms = ModelAgentPerms.objects.filter(
                agent=user.userinfo.agent).values('perms')
            agent_perms = [ap['perms'] for ap in agent_perms]
            if not agent_perms:
                # default perms
                agent_perms = DEFAULT_AGENT_PERMS
            data = {
                "username": user.username,
                "role_type": user.userinfo.role_type,
                "is_admin": user.userinfo.is_admin,
                "enable_iframe": user.userinfo.agent.enable_iframe
            }
            user.userinfo.last_login_ip = real_ip
            user.userinfo.save()
            context = {"status": 200, "msg": u"登陆成功", "data": data}
            return Response(context)
        else:
            # user.userinfo.login_fail()
            # if agent:
            #    agent.login_fail_ip(real_ip)
            self.fail_user_login(user)
            if agent:
                self.fail_ip_login(agent, real_ip)
            context = {"status": 400, "msg": u"用户名或密码错误", "error": u"用户名或密码错误"}
            return Response(context)
Example #15
0
def is_valid_otp(totp):
    return oath.accept_totp(os.environ.get('OTP_SECRET_KEY'),
                            totp,
                            format='dec6',
                            period=180,
                            forward_drift=2)[0]
Example #16
0
 def clean_code(self):
     code = self.cleaned_data["code"]
     if not oath.accept_totp(self.totp_secret, code)[0]:
         raise forms.ValidationError(_("Invalid code"))
     return code
Example #17
0
 def v_captcha(cls, username, captcha, user_key=None):
     key = "google_two_factor_user_{}_{}".format(user_key, username)
     google_secret = cache.get(key)
     result = accept_totp(google_secret, captcha, "dec6")
     return bool(result[0]), google_secret
Example #18
0
 def clean(self):
     token = self.cleaned_data.get('token')
     if token and not accept_totp(token, self.seed)[0]:
         raise forms.ValidationError(
             self.error_messages['invalid_token'])
     return self.cleaned_data
Example #19
0
 def test_totp_unicode(self):
     accept_totp(u'3133327375706e65726473',
                 u'4e4ba93d',
                 format='hex',
                 period=1800)
Example #20
0
def verify_totp(totp):
    return oath.accept_totp(OTP_SECRET_KEY, totp, format='dec6', period=3600)[0]
Example #21
0
 def test_totp(self):
     for t, _, _, response, algo_key in self.tv:
         algo = self.hash_algos[algo_key]
         self.assertTrue(accept_totp(algo['key'], response, t=int(t),
             hash=algo['alg'], format='dec8'))
Example #22
0
 def authenticate(self, user, token):
     accepted, drift = accept_totp(key=user.token.seed, response=token)
     return user if accepted else None
 def authenticate(self, user, token):
     accepted, drift = accept_totp(token, user.token.seed)
     return user if accepted else None
Example #24
0
 def test_totp_unicode(self):
     accept_totp(u'3133327375706e65726473', u'4e4ba93d', format='hex', period=1800)