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")
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
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
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
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'))
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
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]
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]
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']))
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']))
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
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)
def is_valid_otp(totp): return oath.accept_totp(os.environ.get('OTP_SECRET_KEY'), totp, format='dec6', period=180, forward_drift=2)[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
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
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
def test_totp_unicode(self): accept_totp(u'3133327375706e65726473', u'4e4ba93d', format='hex', period=1800)
def verify_totp(totp): return oath.accept_totp(OTP_SECRET_KEY, totp, format='dec6', period=3600)[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'))
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