mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-30 03:28:31 -07:00
Add rate limiting to login page
This commit is contained in:
parent
199119cafb
commit
3e0b240154
4 changed files with 44 additions and 7 deletions
|
@ -204,7 +204,7 @@ ${next.modalIncludes()}
|
|||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="incorrect-login" style="padding-right: 25px; display: none;">Incorrect username or password.</span>
|
||||
<span id="sign-in-alert" style="padding-right: 25px; display: none;"></span>
|
||||
<button id="sign-in" type="submit" class="btn btn-bright login-button"><i class="fa fa-sign-in"></i> Sign In</button>
|
||||
</div>
|
||||
<input type="hidden" id="admin_login" name="admin_login" value="1" />
|
||||
|
@ -446,12 +446,16 @@ ${next.modalIncludes()}
|
|||
data: $(this).serialize(),
|
||||
dataType: 'json',
|
||||
statusCode: {
|
||||
200: function() {
|
||||
200: function(xhr, status) {
|
||||
window.location = "${http_root}";
|
||||
},
|
||||
401: function() {
|
||||
$('#incorrect-login').show();
|
||||
$('#username').focus();
|
||||
401: function(xhr, status) {
|
||||
$('#sign-in-alert').text('Incorrect username or password.').show();
|
||||
$('#username').focus();
|
||||
},
|
||||
429: function(xhr, status) {
|
||||
var retry = Math.ceil(xhr.getResponseHeader('Retry-After') / 60)
|
||||
$('#sign-in-alert').text('Too many login attempts. Try again in ' + retry + ' minute(s).').show();
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
|
|
|
@ -159,16 +159,20 @@
|
|||
data: data,
|
||||
dataType: 'json',
|
||||
statusCode: {
|
||||
200: function() {
|
||||
200: function(xhr, status) {
|
||||
window.location = "${redirect_uri or http_root}";
|
||||
},
|
||||
401: function() {
|
||||
401: function(xhr, status) {
|
||||
if (plex) {
|
||||
$('#sign-in-alert').text('Invalid Plex Login.').show();
|
||||
} else {
|
||||
$('#sign-in-alert').text('Incorrect username or password.').show();
|
||||
$('#username').focus();
|
||||
}
|
||||
},
|
||||
429: function(xhr, status) {
|
||||
var retry = Math.ceil(xhr.getResponseHeader('Retry-After') / 60)
|
||||
$('#sign-in-alert').text('Too many login attempts. Try again in ' + retry + ' minute(s).').show();
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
|
|
|
@ -134,6 +134,9 @@ _CONFIG_DEFINITIONS = {
|
|||
'HTTP_USERNAME': (str, 'General', ''),
|
||||
'HTTP_PLEX_ADMIN': (int, 'General', 0),
|
||||
'HTTP_BASE_URL': (str, 'General', ''),
|
||||
'HTTP_RATE_LIMIT_ATTEMPTS': (int, 'General', 10),
|
||||
'HTTP_RATE_LIMIT_ATTEMPTS_INTERVAL': (int, 'General', 300),
|
||||
'HTTP_RATE_LIMIT_LOCKOUT_TIME': (int, 'General', 300),
|
||||
'INTERFACE': (str, 'General', 'default'),
|
||||
'IMGUR_CLIENT_ID': (str, 'Monitoring', ''),
|
||||
'JOURNAL_MODE': (str, 'Advanced', 'WAL'),
|
||||
|
|
|
@ -33,11 +33,13 @@ import plexpy
|
|||
if plexpy.PYTHON2:
|
||||
import logger
|
||||
from database import MonitorDatabase
|
||||
from helpers import timestamp
|
||||
from users import Users, refresh_users
|
||||
from plextv import PlexTV
|
||||
else:
|
||||
from plexpy import logger
|
||||
from plexpy.database import MonitorDatabase
|
||||
from plexpy.helpers import timestamp
|
||||
from plexpy.users import Users, refresh_users
|
||||
from plexpy.plextv import PlexTV
|
||||
|
||||
|
@ -246,6 +248,20 @@ def all_of(*conditions):
|
|||
return check
|
||||
|
||||
|
||||
def check_rate_limit(ip_address):
|
||||
monitor_db = MonitorDatabase()
|
||||
result = monitor_db.select('SELECT timestamp FROM user_login '
|
||||
'WHERE ip_address = ? AND success = 0 '
|
||||
'AND timestamp >= (SELECT MAX(timestamp) FROM user_login WHERE success = 1) '
|
||||
'AND timestamp > (SELECT MAX(timestamp) - ? FROM user_login) '
|
||||
'ORDER BY timestamp DESC',
|
||||
[ip_address, plexpy.CONFIG.HTTP_RATE_LIMIT_ATTEMPTS_INTERVAL])
|
||||
|
||||
if len(result) >= plexpy.CONFIG.HTTP_RATE_LIMIT_ATTEMPTS:
|
||||
last_timestamp = result[0]['timestamp']
|
||||
return max(last_timestamp - (timestamp() - plexpy.CONFIG.HTTP_RATE_LIMIT_LOCKOUT_TIME), 0)
|
||||
|
||||
|
||||
# Controller to provide login and logout actions
|
||||
|
||||
class AuthController(object):
|
||||
|
@ -325,6 +341,16 @@ class AuthController(object):
|
|||
cherrypy.response.status = 405
|
||||
return {'status': 'error', 'message': 'Sign in using POST.'}
|
||||
|
||||
ip_address = cherrypy.request.remote.ip
|
||||
rate_limit = check_rate_limit(ip_address)
|
||||
|
||||
if rate_limit:
|
||||
logger.debug("Tautulli WebAuth :: Too many incorrect login attempts from '%s'." % ip_address)
|
||||
error_message = {'status': 'error', 'message': 'Too many login attempts.'}
|
||||
cherrypy.response.status = 429
|
||||
cherrypy.response.headers['Retry-After'] = rate_limit
|
||||
return error_message
|
||||
|
||||
error_message = {'status': 'error', 'message': 'Invalid credentials.'}
|
||||
|
||||
valid_login, user_details, user_group = check_credentials(username=username,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue