Skip to content
Snippets Groups Projects
Commit 739016ee authored by ale's avatar ale
Browse files

add a handler for NGINX mail_auth_http module

parent 27382f91
Branches
No related tags found
No related merge requests found
......@@ -144,7 +144,8 @@ class BlackList(object):
mc.set(key, 'true', time=self.ttl)
def blacklist_on_auth_failure(key_fn, count=0, period=0, ttl=0, check_wl=False):
def blacklist_on_auth_failure(key_fn, count=0, period=0, ttl=0, check_wl=False,
bl_return_value=protocol.ERR_AUTHENTICATION_FAILURE):
"""Blacklist authentication failures.
The wrapped function should return one of the error codes from
......@@ -168,7 +169,7 @@ def blacklist_on_auth_failure(key_fn, count=0, period=0, ttl=0, check_wl=False):
if ((not check_wl or not whitelisted(key))
and not bl.check(app.memcache, key)):
app.logger.debug('blacklisted %s', key)
return protocol.ERR_AUTHENTICATION_FAILURE
return bl_return_value
result = fn(*args, **kwargs)
if result != protocol.OK:
bl.incr(app.memcache, key)
......
......@@ -9,15 +9,18 @@ from authserv import protocol
from authserv.ratelimit import *
from flask import Flask, request, abort, make_response
_blacklisted = (protocol.ERR_AUTHENTICATION_FAILURE, None)
@blacklist_on_auth_failure(key_from_args(0), count=5, period=600, ttl=43200)
@blacklist_on_auth_failure(key_from_args(0), count=5, period=600, ttl=43200,
bl_return_value=_blacklisted)
@blacklist_on_auth_failure(key_from_args(5), count=5, period=600, ttl=43200,
check_wl=True)
check_wl=True, bl_return_value=_blacklisted)
def _auth(username, service, shard, password, otp_token, source_ip):
user = app.userdb.get_user(username, service, shard)
if not user:
return protocol.ERR_AUTHENTICATION_FAILURE
return auth.authenticate(user, service, password, otp_token)
return _blacklisted
return (auth.authenticate(user, service, password, otp_token),
user.get_shard())
# Quick clarification on the rate limits: 'username' is the one that's
......@@ -47,7 +50,7 @@ def do_auth():
abort(400)
try:
result = _auth(username, service, shard, password, otp_token, source_ip)
result, _ = _auth(username, service, shard, password, otp_token, source_ip)
except Exception, e:
app.logger.exception('Unexpected exception in authenticate()')
abort(500)
......@@ -63,6 +66,40 @@ def do_auth():
return response
_default_port_map = {'imap': 143, 'pop3': 110}
@app.route('/auth', methods=('GET',))
def do_nginx_http_auth():
service = app.config.get('NGINX_AUTH_SERVICE', 'mail')
username = request.environ.get('HTTP_AUTH_USER')
password = request.environ.get('HTTP_AUTH_PASS')
source_ip = request.environ.get('HTTP_CLIENT_IP')
protocol = request.environ.get('HTTP_AUTH_PROTOCOL')
try:
n_attempt = int(request.environ.get('HTTP_AUTH_LOGIN_ATTEMPT'))
except ValueError:
n_attempt = 1
try:
auth_status, shard = _auth(
username, service, None, password, None, source_ip)
except Exception, e:
app.logger.exception('Unexpected exception in authenticate()')
abort(500)
response = make_response('')
if auth_status == 'OK':
response.headers['Auth-Status'] = 'OK'
response.headers['Auth-Server'] = shard
response.headers['Auth-Port'] = str(
app.config.get('NGINX_AUTH_PORT_MAP', _default_port_map)[protocol])
else:
response.headers['Auth-Status'] = 'Invalid login or password'
if n_attempt <= 3:
response.headers['Auth-Wait'] = '3'
return response
def create_app(userdb=None, mc=None):
app.config.from_envvar('APP_CONFIG', silent=True)
......
......@@ -183,3 +183,27 @@ class ServerTest(unittest.TestCase):
self.assertEquals(200, response.status_code)
self.assertEquals(protocol.OK, response.data)
def test_nginx_http_auth_ok(self):
response = self.app.get(
'/auth', headers={
'Auth-User': 'user',
'Auth-Pass': 'pass',
'Client-IP': '127.0.0.1',
'Auth-Protocol': 'imap',
'Auth-Login-Attempt': '1',
})
self.assertEquals(200, response.status_code)
self.assertEquals('OK', response.headers['Auth-Status'])
def test_nginx_http_auth_fail(self):
response = self.app.get(
'/auth', headers={
'Auth-User': 'user',
'Auth-Pass': 'bad password',
'Client-IP': '127.0.0.1',
'Auth-Protocol': 'imap',
'Auth-Login-Attempt': '1',
})
self.assertEquals(200, response.status_code)
self.assertNotEquals('OK', response.headers['Auth-Status'])
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment