mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 05:31:15 -07:00
Log failed login attempts
This commit is contained in:
parent
daec864f50
commit
39da58d3bc
5 changed files with 63 additions and 17 deletions
|
@ -77,9 +77,24 @@ login_log_table_options = {
|
||||||
{
|
{
|
||||||
"targets": [6],
|
"targets": [6],
|
||||||
"data": "browser",
|
"data": "browser",
|
||||||
"width": "20%",
|
"width": "18%",
|
||||||
"className": "no-wrap"
|
"className": "no-wrap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"targets": [7],
|
||||||
|
"data": "success",
|
||||||
|
"createdCell": function (td, cellData, rowData, row, col) {
|
||||||
|
if (cellData == 1) {
|
||||||
|
$(td).html('<span class="success-tooltip" data-toggle="tooltip" title="Login Successful"><i class="fa fa-lg fa-fw fa-check"></i></span>');
|
||||||
|
} else {
|
||||||
|
$(td).html('<span class="success-tooltip" data-toggle="tooltip" title="Login Failed"><i class="fa fa-lg fa-fw fa-times"></i></span>');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"searchable": false,
|
||||||
|
"orderable": false,
|
||||||
|
"className": "no-wrap",
|
||||||
|
"width": "2%"
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"drawCallback": function (settings) {
|
"drawCallback": function (settings) {
|
||||||
// Jump to top of page
|
// Jump to top of page
|
||||||
|
|
|
@ -145,13 +145,14 @@
|
||||||
<table class="display login_log_table" id="login_log_table" width="100%">
|
<table class="display login_log_table" id="login_log_table" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th align="left" id="timestamp">Timestamp</th>
|
<th align="left" id="login_timestamp">Timestamp</th>
|
||||||
<th align="left" id="friendly_name">User</th>
|
<th align="left" id="login_friendly_name">User</th>
|
||||||
<th align="left" id="user_group">User Group</th>
|
<th align="left" id="login_user_group">User Group</th>
|
||||||
<th align="left" id="ip_address">IP Address</th>
|
<th align="left" id="login_ip_address">IP Address</th>
|
||||||
<th align="left" id="host">Host</th>
|
<th align="left" id="login_host">Host</th>
|
||||||
<th align="left" id="os">Operating System</th>
|
<th align="left" id="login_os">Operating System</th>
|
||||||
<th align="left" id="browser">Browser</th>
|
<th align="left" id="login_browser">Browser</th>
|
||||||
|
<th align="left" id="login_success"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
|
|
|
@ -496,7 +496,8 @@ def dbcheck():
|
||||||
# user_login table :: This table keeps record of the PlexPy guest logins
|
# user_login table :: This table keeps record of the PlexPy guest logins
|
||||||
c_db.execute(
|
c_db.execute(
|
||||||
'CREATE TABLE IF NOT EXISTS user_login (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
'CREATE TABLE IF NOT EXISTS user_login (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||||
'timestamp INTEGER, user_id INTEGER, user TEXT, user_group TEXT, ip_address TEXT, host TEXT, user_agent TEXT)'
|
'timestamp INTEGER, user_id INTEGER, user TEXT, user_group TEXT, '
|
||||||
|
'ip_address TEXT, host TEXT, user_agent TEXT, success INTEGER DEFAULT 1)'
|
||||||
)
|
)
|
||||||
|
|
||||||
# notifiers table :: This table keeps record of the notification agent settings
|
# notifiers table :: This table keeps record of the notification agent settings
|
||||||
|
@ -1124,6 +1125,15 @@ def dbcheck():
|
||||||
'DROP INDEX IF EXISTS idx_themoviedb_lookup_imdb_id'
|
'DROP INDEX IF EXISTS idx_themoviedb_lookup_imdb_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Upgrade user_login table from earlier versions
|
||||||
|
try:
|
||||||
|
c_db.execute('SELECT success FROM user_login')
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
logger.debug(u"Altering database. Updating database table user_login.")
|
||||||
|
c_db.execute(
|
||||||
|
'ALTER TABLE user_login ADD COLUMN success INTEGER DEFAULT 1'
|
||||||
|
)
|
||||||
|
|
||||||
# Add "Local" user to database as default unauthenticated user.
|
# Add "Local" user to database as default unauthenticated user.
|
||||||
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
|
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
|
||||||
if not result.fetchone():
|
if not result.fetchone():
|
||||||
|
|
|
@ -678,7 +678,7 @@ class Users(object):
|
||||||
|
|
||||||
return filters_list
|
return filters_list
|
||||||
|
|
||||||
def set_user_login(self, user_id=None, user=None, user_group=None, ip_address=None, host=None, user_agent=None):
|
def set_user_login(self, user_id=None, user=None, user_group=None, ip_address=None, host=None, user_agent=None, success=0):
|
||||||
|
|
||||||
if user_id is None or str(user_id).isdigit():
|
if user_id is None or str(user_id).isdigit():
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
@ -690,7 +690,8 @@ class Users(object):
|
||||||
'user_group': user_group,
|
'user_group': user_group,
|
||||||
'ip_address': ip_address,
|
'ip_address': ip_address,
|
||||||
'host': host,
|
'host': host,
|
||||||
'user_agent': user_agent}
|
'user_agent': user_agent,
|
||||||
|
'success': success}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
monitor_db.upsert(table_name='user_login', key_dict=keys, value_dict=values)
|
monitor_db.upsert(table_name='user_login', key_dict=keys, value_dict=values)
|
||||||
|
@ -714,13 +715,14 @@ class Users(object):
|
||||||
else:
|
else:
|
||||||
custom_where = [['user_login.user_id', user_id]] if user_id else []
|
custom_where = [['user_login.user_id', user_id]] if user_id else []
|
||||||
|
|
||||||
columns = ['user_login.user_id',
|
columns = ['user_login.timestamp',
|
||||||
|
'user_login.user_id',
|
||||||
'user_login.user',
|
'user_login.user',
|
||||||
'user_login.user_group',
|
'user_login.user_group',
|
||||||
'user_login.ip_address',
|
'user_login.ip_address',
|
||||||
'user_login.host',
|
'user_login.host',
|
||||||
'user_login.user_agent',
|
'user_login.user_agent',
|
||||||
'user_login.timestamp',
|
'user_login.success',
|
||||||
'(CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = "" \
|
'(CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = "" \
|
||||||
THEN users.username ELSE users.friendly_name END) AS friendly_name'
|
THEN users.username ELSE users.friendly_name END) AS friendly_name'
|
||||||
]
|
]
|
||||||
|
@ -744,14 +746,15 @@ class Users(object):
|
||||||
for item in results:
|
for item in results:
|
||||||
(os, browser) = httpagentparser.simple_detect(item['user_agent'])
|
(os, browser) = httpagentparser.simple_detect(item['user_agent'])
|
||||||
|
|
||||||
row = {'user_id': item['user_id'],
|
row = {'timestamp': item['timestamp'],
|
||||||
|
'user_id': item['user_id'],
|
||||||
'user_group': item['user_group'],
|
'user_group': item['user_group'],
|
||||||
'ip_address': item['ip_address'],
|
'ip_address': item['ip_address'],
|
||||||
'host': item['host'],
|
'host': item['host'],
|
||||||
'user_agent': item['user_agent'],
|
'user_agent': item['user_agent'],
|
||||||
'os': os,
|
'os': os,
|
||||||
'browser': browser,
|
'browser': browser,
|
||||||
'timestamp': item['timestamp'],
|
'success': item['success'],
|
||||||
'friendly_name': item['friendly_name'] or item['user']
|
'friendly_name': item['friendly_name'] or item['user']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,8 @@ class AuthController(object):
|
||||||
user_group=user_group,
|
user_group=user_group,
|
||||||
ip_address=ip_address,
|
ip_address=ip_address,
|
||||||
host=host,
|
host=host,
|
||||||
user_agent=user_agent)
|
user_agent=user_agent,
|
||||||
|
success=1)
|
||||||
|
|
||||||
logger.debug(u"PlexPy WebAuth :: %s user '%s' logged into PlexPy." % (user_group.capitalize(), username))
|
logger.debug(u"PlexPy WebAuth :: %s user '%s' logged into PlexPy." % (user_group.capitalize(), username))
|
||||||
|
|
||||||
|
@ -198,6 +199,20 @@ class AuthController(object):
|
||||||
"""Called on logout"""
|
"""Called on logout"""
|
||||||
logger.debug(u"PlexPy WebAuth :: %s User '%s' logged out of PlexPy." % (user_group.capitalize(), username))
|
logger.debug(u"PlexPy WebAuth :: %s User '%s' logged out of PlexPy." % (user_group.capitalize(), username))
|
||||||
|
|
||||||
|
def on_login_failed(self, username):
|
||||||
|
"""Called on failed login"""
|
||||||
|
|
||||||
|
# Save login attempt to the database
|
||||||
|
ip_address = cherrypy.request.headers.get('X-Forwarded-For', cherrypy.request.headers.get('Remote-Addr'))
|
||||||
|
host = cherrypy.request.headers.get('Origin')
|
||||||
|
user_agent = cherrypy.request.headers.get('User-Agent')
|
||||||
|
|
||||||
|
Users().set_user_login(user=username,
|
||||||
|
ip_address=ip_address,
|
||||||
|
host=host,
|
||||||
|
user_agent=user_agent,
|
||||||
|
success=0)
|
||||||
|
|
||||||
def get_loginform(self, username="", msg=""):
|
def get_loginform(self, username="", msg=""):
|
||||||
from plexpy.webserve import serve_template
|
from plexpy.webserve import serve_template
|
||||||
return serve_template(templatename="login.html", title="Login", username=escape(username, True), msg=msg)
|
return serve_template(templatename="login.html", title="Login", username=escape(username, True), msg=msg)
|
||||||
|
@ -240,9 +255,11 @@ class AuthController(object):
|
||||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||||
|
|
||||||
elif admin_login == '1':
|
elif admin_login == '1':
|
||||||
|
self.on_login_failed(username)
|
||||||
logger.debug(u"PlexPy WebAuth :: Invalid admin login attempt from '%s'." % username)
|
logger.debug(u"PlexPy WebAuth :: Invalid admin login attempt from '%s'." % username)
|
||||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||||
else:
|
else:
|
||||||
|
self.on_login_failed(username)
|
||||||
logger.debug(u"PlexPy WebAuth :: Invalid login attempt from '%s'." % username)
|
logger.debug(u"PlexPy WebAuth :: Invalid login attempt from '%s'." % username)
|
||||||
return self.get_loginform(username, u"Incorrect username/email or password.")
|
return self.get_loginform(username, u"Incorrect username/email or password.")
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue