Skip to content
Snippets Groups Projects
Commit 6eaea794 authored by ale's avatar ale
Browse files

add a method to test passwords even if OTP is enabled

This method is meant to be used for auxiliary authentication for
privileged operations where the user is asked for his current
password (like a password change).
parent 59e04fff
No related branches found
No related tags found
No related merge requests found
...@@ -22,7 +22,8 @@ def check_ratelimit(request, username, source_ip): ...@@ -22,7 +22,8 @@ def check_ratelimit(request, username, source_ip):
abort(503) abort(503)
def do_auth(username, service, shard, password, otp_token, source_ip): def do_auth(username, service, shard, password, otp_token, source_ip,
password_only=False):
bl = AuthBlackList(current_app.config.get('BLACKLIST_COUNT', 5), bl = AuthBlackList(current_app.config.get('BLACKLIST_COUNT', 5),
current_app.config.get('BLACKLIST_PERIOD', 600), current_app.config.get('BLACKLIST_PERIOD', 600),
current_app.config.get('BLACKLIST_TIME', 6*3600)) current_app.config.get('BLACKLIST_TIME', 6*3600))
...@@ -40,7 +41,7 @@ def do_auth(username, service, shard, password, otp_token, source_ip): ...@@ -40,7 +41,7 @@ def do_auth(username, service, shard, password, otp_token, source_ip):
user = current_app.userdb.get_user(username, service, shard) user = current_app.userdb.get_user(username, service, shard)
if user: if user:
retval, errmsg = auth.authenticate( retval, errmsg = auth.authenticate(
user, service, password, otp_token, source_ip) user, service, password, otp_token, source_ip, password_only)
out_shard = user.get_shard() out_shard = user.get_shard()
if shard and out_shard != shard: if shard and out_shard != shard:
retval = protocol.ERR_AUTHENTICATION_FAILURE retval = protocol.ERR_AUTHENTICATION_FAILURE
......
...@@ -51,3 +51,27 @@ def api_auth(): ...@@ -51,3 +51,27 @@ def api_auth():
response.headers['Content-Type'] = 'text/plain' response.headers['Content-Type'] = 'text/plain'
response.headers['Expires'] = '-1' response.headers['Expires'] = '-1'
return response return response
@app.route('/api/1/auth_pwonly', methods=('POST',))
def api_auth_pwonly():
username = request.form.get('username')
password = request.form.get('password')
if not username:
abort(400)
try:
auth_status, errmsg, unused_shard = do_auth(
username, None, None, password, None, None,
password_only=True)
except Exception, e:
app.logger.exception('Unexpected exception in auth_pwonly()')
abort(500)
response = make_response(auth_status)
response.headers['Cache-Control'] = 'no-cache'
response.headers['Content-Type'] = 'text/plain'
response.headers['Expires'] = '-1'
return response
...@@ -30,13 +30,15 @@ def _check_otp(totp_key, token): ...@@ -30,13 +30,15 @@ def _check_otp(totp_key, token):
return protocol.ERR_AUTHENTICATION_FAILURE return protocol.ERR_AUTHENTICATION_FAILURE
def authenticate(user, service, password, otp_token, source_ip=None): def authenticate(user, service, password, otp_token, source_ip=None,
password_only=False):
if not password: if not password:
return protocol.ERR_AUTHENTICATION_FAILURE, 'empty password' return protocol.ERR_AUTHENTICATION_FAILURE, 'empty password'
if isinstance(password, unicode): if isinstance(password, unicode):
password = password.encode('utf-8') password = password.encode('utf-8')
if not password_only:
# If the account has app-specific passwords, do not allow login # If the account has app-specific passwords, do not allow login
# with the standard password unless it comes from localhost (so # with the standard password unless it comes from localhost (so
# that the old-crappy-SSO can successfully pass the password along # that the old-crappy-SSO can successfully pass the password along
......
from authserv.test import * from authserv.test import *
from authserv.ratelimit import * from authserv.ratelimit import *
from authserv.oath import totp
from authserv import protocol from authserv import protocol
from authserv import server from authserv import server
from authserv import app_main from authserv import app_main
URL = '/api/1/auth' URL = '/api/1/auth'
URL_PWONLY = '/api/1/auth_pwonly'
class ServerTest(unittest.TestCase): class ServerTest(unittest.TestCase):
...@@ -15,6 +17,7 @@ class ServerTest(unittest.TestCase): ...@@ -15,6 +17,7 @@ class ServerTest(unittest.TestCase):
return self.tick return self.tick
self.users = { self.users = {
'user': FakeUser('user', 'pass', shard='a'), 'user': FakeUser('user', 'pass', shard='a'),
'otpuser': FakeUser('otpuser', 'pass', otp_key='1234'),
} }
app = server.create_app(app_main.app, app = server.create_app(app_main.app,
userdb=FakeUserDb(self.users), userdb=FakeUserDb(self.users),
...@@ -35,6 +38,16 @@ class ServerTest(unittest.TestCase): ...@@ -35,6 +38,16 @@ class ServerTest(unittest.TestCase):
'service': 'svc'}) 'service': 'svc'})
self.assertEquals(protocol.OK, response.data) self.assertEquals(protocol.OK, response.data)
def test_auth_otp_ok(self):
token = totp('1234', format='dec6', period=30)
response = self.app.post(
URL, data={
'username': 'otpuser',
'password': 'pass',
'service': 'svc',
'otp': token})
self.assertEquals(protocol.OK, response.data)
def test_auth_simple_fail(self): def test_auth_simple_fail(self):
response = self.app.post( response = self.app.post(
URL, data={ URL, data={
...@@ -205,3 +218,25 @@ class ServerTest(unittest.TestCase): ...@@ -205,3 +218,25 @@ class ServerTest(unittest.TestCase):
'service': 'svc', 'source_ip': '127.0.0.1'}) 'service': 'svc', 'source_ip': '127.0.0.1'})
self.assertEquals(200, response.status_code) self.assertEquals(200, response.status_code)
self.assertEquals(protocol.OK, response.data) self.assertEquals(protocol.OK, response.data)
def test_auth_pwonly_simple_ok(self):
response = self.app.post(
URL_PWONLY, data={
'username': 'user',
'password': 'pass'})
self.assertEquals(protocol.OK, response.data)
def test_auth_pwonly_otp_ok(self):
response = self.app.post(
URL_PWONLY, data={
'username': 'otpuser',
'password': 'pass'})
self.assertEquals(protocol.OK, response.data)
def test_auth_pwonly_simple_fail(self):
response = self.app.post(
URL_PWONLY, data={
'username': 'user',
'password': 'badpass'})
self.assertEquals(protocol.ERR_AUTHENTICATION_FAILURE,
response.data)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment