Allow Plex admin to login as Tautulli admin

This commit is contained in:
JonnyWong16 2018-01-11 22:58:30 -08:00
parent 3f90037db3
commit 896a37bea9
8 changed files with 113 additions and 54 deletions

View file

@ -472,6 +472,13 @@
</div> </div>
<input type="text" id="http_hashed_password" name="http_hashed_password" value="${config['http_hashed_password']}" style="display: none;" data-parsley-trigger="change" data-parsley-type="integer" data-parsley-range="[0, 1]" <input type="text" id="http_hashed_password" name="http_hashed_password" value="${config['http_hashed_password']}" style="display: none;" data-parsley-trigger="change" data-parsley-type="integer" data-parsley-range="[0, 1]"
data-parsley-errors-container="#http_hash_password_error" data-parsley-error-message="Cannot un-hash password, please set a new password." data-parsley-no-focus required> data-parsley-errors-container="#http_hash_password_error" data-parsley-error-message="Cannot un-hash password, please set a new password." data-parsley-no-focus required>
<div class="checkbox">
<label>
<input type="checkbox" class="auth-settings" name="http_plex_admin" id="http_plex_admin" value="1" ${config['http_plex_admin']} data-parsley-trigger="change"> Allow Plex Admin
</label>
<span id="allowPlexCheck" style="color: #eb8600; padding-left: 10px;"></span>
<p class="help-block">Allow the Plex server admin to login as a Tautulli admin using their Plex.tv account.</p>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" class="auth-settings" name="http_basic_auth" id="http_basic_auth" value="1" ${config['http_basic_auth']} data-parsley-trigger="change"> Use Basic Authentication <input type="checkbox" class="auth-settings" name="http_basic_auth" id="http_basic_auth" value="1" ${config['http_basic_auth']} data-parsley-trigger="change"> Use Basic Authentication
@ -479,6 +486,7 @@
<p class="help-block">Use basic HTTP authentication instead of the HTML login form.</p> <p class="help-block">Use basic HTTP authentication instead of the HTML login form.</p>
</div> </div>
<input type="checkbox" name="auth_changed" id="auth_changed" value="1" style="display: none;">
<div class="padded-header"> <div class="padded-header">
<h3>Guest Access</h3> <h3>Guest Access</h3>
@ -1777,6 +1785,7 @@ $(document).ready(function() {
$( ".auth-settings" ).change(function() { $( ".auth-settings" ).change(function() {
authChanged = true; authChanged = true;
$("#auth_changed").prop('checked', true);
}); });
$( ".directory-settings" ).change(function() { $( ".directory-settings" ).change(function() {
@ -2016,6 +2025,26 @@ $(document).ready(function() {
} }
}); });
function allowPlexAdminCheck () {
if ($("#http_basic_auth").is(":checked")) {
$("#http_plex_admin").attr("disabled", true);
$("#http_plex_admin").attr("checked", false);
$("#allowPlexCheck").html("Plex admin login cannot be enabled with basic authentication.");
} else if ($('#http_username').val() == '' || $('#http_password').val() == '') {
$("#http_plex_admin").attr("disabled", true);
$("#http_plex_admin").attr("checked", false);
$("#allowPlexCheck").html("You must set an admin username and password above to allow Plex admin login.");
} else {
$("#http_plex_admin").attr("disabled", false);
$("#allowPlexCheck").html("");
}
}
allowPlexAdminCheck();
$('#http_username, #http_password, #http_basic_auth').change(function () {
allowPlexAdminCheck();
});
function allowGuestAccessCheck () { function allowGuestAccessCheck () {
if ($("#http_basic_auth").is(":checked")) { if ($("#http_basic_auth").is(":checked")) {
$("#allow_guest_access").attr("disabled", true); $("#allow_guest_access").attr("disabled", true);
@ -2024,7 +2053,7 @@ $(document).ready(function() {
} else if ($('#http_username').val() == '' || $('#http_password').val() == '') { } else if ($('#http_username').val() == '' || $('#http_password').val() == '') {
$("#allow_guest_access").attr("disabled", true); $("#allow_guest_access").attr("disabled", true);
$("#allow_guest_access").attr("checked", false); $("#allow_guest_access").attr("checked", false);
$("#allowGuestCheck").html("You must set an admin password above to allow guest access."); $("#allowGuestCheck").html("You must set an admin username and password above to allow guest access.");
} else { } else {
$("#allow_guest_access").attr("disabled", false); $("#allow_guest_access").attr("disabled", false);
$("#allowGuestCheck").html(""); $("#allowGuestCheck").html("");

View file

@ -502,7 +502,7 @@ def dbcheck():
c_db.execute( c_db.execute(
'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'user_id INTEGER DEFAULT NULL UNIQUE, username TEXT NOT NULL, friendly_name TEXT, ' 'user_id INTEGER DEFAULT NULL UNIQUE, username TEXT NOT NULL, friendly_name TEXT, '
'thumb TEXT, custom_avatar_url TEXT, email TEXT, is_home_user INTEGER DEFAULT NULL, ' 'thumb TEXT, custom_avatar_url TEXT, email TEXT, is_admin INTEGER DEFAULT 0, is_home_user INTEGER DEFAULT NULL, '
'is_allow_sync INTEGER DEFAULT NULL, is_restricted INTEGER DEFAULT NULL, do_notify INTEGER DEFAULT 1, ' 'is_allow_sync INTEGER DEFAULT NULL, is_restricted INTEGER DEFAULT NULL, do_notify INTEGER DEFAULT 1, '
'keep_history INTEGER DEFAULT 1, deleted_user INTEGER DEFAULT 0, allow_guest INTEGER DEFAULT 0, ' 'keep_history INTEGER DEFAULT 1, deleted_user INTEGER DEFAULT 0, allow_guest INTEGER DEFAULT 0, '
'user_token TEXT, server_token TEXT, shared_libraries TEXT, filter_all TEXT, filter_movies TEXT, filter_tv TEXT, ' 'user_token TEXT, server_token TEXT, shared_libraries TEXT, filter_all TEXT, filter_movies TEXT, filter_tv TEXT, '
@ -1289,6 +1289,15 @@ def dbcheck():
'ALTER TABLE users ADD COLUMN filter_photos TEXT' 'ALTER TABLE users ADD COLUMN filter_photos TEXT'
) )
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT is_admin FROM users')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table users.")
c_db.execute(
'ALTER TABLE users ADD COLUMN is_admin INTEGER DEFAULT 0'
)
# Upgrade notify_log table from earlier versions # Upgrade notify_log table from earlier versions
try: try:
c_db.execute('SELECT poster_url FROM notify_log') c_db.execute('SELECT poster_url FROM notify_log')

View file

@ -225,6 +225,7 @@ _CONFIG_DEFINITIONS = {
'HTTP_PROXY': (int, 'General', 0), 'HTTP_PROXY': (int, 'General', 0),
'HTTP_ROOT': (str, 'General', ''), 'HTTP_ROOT': (str, 'General', ''),
'HTTP_USERNAME': (str, 'General', ''), 'HTTP_USERNAME': (str, 'General', ''),
'HTTP_PLEX_ADMIN': (int, 'General', 0),
'HIPCHAT_URL': (str, 'Hipchat', ''), 'HIPCHAT_URL': (str, 'Hipchat', ''),
'HIPCHAT_COLOR': (str, 'Hipchat', ''), 'HIPCHAT_COLOR': (str, 'Hipchat', ''),
'HIPCHAT_INCL_SUBJECT': (int, 'Hipchat', 1), 'HIPCHAT_INCL_SUBJECT': (int, 'Hipchat', 1),

View file

@ -335,6 +335,7 @@ class PlexTV(object):
"thumb": helpers.get_xml_attr(a, 'thumb'), "thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'), "email": helpers.get_xml_attr(a, 'email'),
"is_home_user": helpers.get_xml_attr(a, 'home'), "is_home_user": helpers.get_xml_attr(a, 'home'),
"is_admin": 1,
"is_allow_sync": None, "is_allow_sync": None,
"is_restricted": helpers.get_xml_attr(a, 'restricted'), "is_restricted": helpers.get_xml_attr(a, 'restricted'),
"filter_all": helpers.get_xml_attr(a, 'filterAll'), "filter_all": helpers.get_xml_attr(a, 'filterAll'),
@ -357,6 +358,7 @@ class PlexTV(object):
"username": helpers.get_xml_attr(a, 'title'), "username": helpers.get_xml_attr(a, 'title'),
"thumb": helpers.get_xml_attr(a, 'thumb'), "thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'), "email": helpers.get_xml_attr(a, 'email'),
"is_admin": 0,
"is_home_user": helpers.get_xml_attr(a, 'home'), "is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": helpers.get_xml_attr(a, 'allowSync'), "is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
"is_restricted": helpers.get_xml_attr(a, 'restricted'), "is_restricted": helpers.get_xml_attr(a, 'restricted'),

View file

@ -52,6 +52,7 @@ def refresh_users():
new_value_dict = {"username": item['username'], new_value_dict = {"username": item['username'],
"thumb": item['thumb'], "thumb": item['thumb'],
"email": item['email'], "email": item['email'],
"is_admin": item['is_admin'],
"is_home_user": item['is_home_user'], "is_home_user": item['is_home_user'],
"is_allow_sync": item['is_allow_sync'], "is_allow_sync": item['is_allow_sync'],
"is_restricted": item['is_restricted'], "is_restricted": item['is_restricted'],
@ -330,6 +331,7 @@ class Users(object):
'friendly_name': 'Local', 'friendly_name': 'Local',
'user_thumb': common.DEFAULT_USER_THUMB, 'user_thumb': common.DEFAULT_USER_THUMB,
'email': '', 'email': '',
'is_admin': '',
'is_home_user': 0, 'is_home_user': 0,
'is_allow_sync': 0, 'is_allow_sync': 0,
'is_restricted': 0, 'is_restricted': 0,
@ -349,21 +351,21 @@ class Users(object):
try: try:
if str(user_id).isdigit(): if str(user_id).isdigit():
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \ query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \ 'email, is_admin, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \ 'allow_guest, shared_libraries ' \
'FROM users ' \ 'FROM users ' \
'WHERE user_id = ? ' 'WHERE user_id = ? '
result = monitor_db.select(query, args=[user_id]) result = monitor_db.select(query, args=[user_id])
elif user: elif user:
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \ query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \ 'email, is_admin, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \ 'allow_guest, shared_libraries ' \
'FROM users ' \ 'FROM users ' \
'WHERE username = ? COLLATE NOCASE ' 'WHERE username = ? COLLATE NOCASE '
result = monitor_db.select(query, args=[user]) result = monitor_db.select(query, args=[user])
elif email: elif email:
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \ query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \ 'email, is_admin, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \ 'allow_guest, shared_libraries ' \
'FROM users ' \ 'FROM users ' \
'WHERE email = ? COLLATE NOCASE ' 'WHERE email = ? COLLATE NOCASE '
@ -398,6 +400,7 @@ class Users(object):
'friendly_name': friendly_name, 'friendly_name': friendly_name,
'user_thumb': user_thumb, 'user_thumb': user_thumb,
'email': item['email'], 'email': item['email'],
'is_admin': item['is_admin'],
'is_home_user': item['is_home_user'], 'is_home_user': item['is_home_user'],
'is_allow_sync': item['is_allow_sync'], 'is_allow_sync': item['is_allow_sync'],
'is_restricted': item['is_restricted'], 'is_restricted': item['is_restricted'],

View file

@ -54,10 +54,17 @@ def user_login(username=None, password=None):
if user_id != str(user_details['user_id']): if user_id != str(user_details['user_id']):
# The user is not in the database. # The user is not in the database.
return None return None
elif plexpy.CONFIG.HTTP_PLEX_ADMIN and user_details['is_admin']:
# Plex admin login
return 'admin'
elif not user_details['allow_guest'] or user_details['deleted_user']: elif not user_details['allow_guest'] or user_details['deleted_user']:
# Guest access is disabled or the user is deleted. # Guest access is disabled or the user is deleted.
return None return None
# Stop here if guest access is not enabled
if not plexpy.CONFIG.ALLOW_GUEST_ACCESS:
return None
# The user is in the database, and guest access is enabled, so try to retrieve a server token. # The user is in the database, and guest access is enabled, so try to retrieve a server token.
# If a server token is returned, then the user is a valid friend of the server. # If a server token is returned, then the user is a valid friend of the server.
plex_tv = PlexTV(token=user_token) plex_tv = PlexTV(token=user_token)
@ -75,7 +82,7 @@ def user_login(username=None, password=None):
# Refresh the users list to make sure we have all the correct permissions. # Refresh the users list to make sure we have all the correct permissions.
refresh_users() refresh_users()
# Successful login # Successful login
return True return 'guest'
else: else:
logger.warn(u"Tautulli WebAuth :: Unable to register user '%s' in database." % username) logger.warn(u"Tautulli WebAuth :: Unable to register user '%s' in database." % username)
return None return None
@ -96,15 +103,19 @@ def check_credentials(username, password, admin_login='0'):
"""Verifies credentials for username and password. """Verifies credentials for username and password.
Returns True and the user group on success or False and no user group""" Returns True and the user group on success or False and no user group"""
if plexpy.CONFIG.HTTP_PASSWORD:
if plexpy.CONFIG.HTTP_HASHED_PASSWORD and \ if plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
username == plexpy.CONFIG.HTTP_USERNAME and check_hash(password, plexpy.CONFIG.HTTP_PASSWORD): username == plexpy.CONFIG.HTTP_USERNAME and check_hash(password, plexpy.CONFIG.HTTP_PASSWORD):
return True, u'admin' return True, 'admin'
elif not plexpy.CONFIG.HTTP_HASHED_PASSWORD and \ elif not plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD: username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD:
return True, u'admin' return True, 'admin'
elif not admin_login == '1' and plexpy.CONFIG.ALLOW_GUEST_ACCESS and user_login(username, password):
return True, u'guest' if plexpy.CONFIG.HTTP_PLEX_ADMIN or (not admin_login == '1' and plexpy.CONFIG.ALLOW_GUEST_ACCESS):
else: plex_login = user_login(username, password)
if plex_login is not None:
return True, plex_login
return False, None return False, None
@ -163,15 +174,12 @@ def requireAuth(*conditions):
# #
# Define those at will however suits the application. # Define those at will however suits the application.
def member_of(groupname): def member_of(user_group):
def check(): return cherrypy.request.login['user_group'] == user_group
# replace with actual check if <username> is in <groupname>
return cherrypy.request.login['user'] == plexpy.CONFIG.HTTP_USERNAME and groupname == 'admin'
return check
def name_is(reqd_username): def name_is(user_name):
return lambda: reqd_username == cherrypy.request.login['user'] return cherrypy.request.login['user'] == user_name
# These might be handy # These might be handy

View file

@ -2538,6 +2538,7 @@ class WebInterface(object):
"http_password": http_password, "http_password": http_password,
"http_root": plexpy.CONFIG.HTTP_ROOT, "http_root": plexpy.CONFIG.HTTP_ROOT,
"http_proxy": checked(plexpy.CONFIG.HTTP_PROXY), "http_proxy": checked(plexpy.CONFIG.HTTP_PROXY),
"http_plex_admin": checked(plexpy.CONFIG.HTTP_PLEX_ADMIN),
"launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER), "launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
"enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS), "enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS),
"https_create_cert": checked(plexpy.CONFIG.HTTPS_CREATE_CERT), "https_create_cert": checked(plexpy.CONFIG.HTTPS_CREATE_CERT),
@ -2632,7 +2633,7 @@ class WebInterface(object):
"monitor_pms_updates", "monitor_remote_access", "get_file_sizes", "log_blacklist", "http_hash_password", "monitor_pms_updates", "monitor_remote_access", "get_file_sizes", "log_blacklist", "http_hash_password",
"allow_guest_access", "cache_images", "http_proxy", "http_basic_auth", "notify_concurrent_by_ip", "allow_guest_access", "cache_images", "http_proxy", "http_basic_auth", "notify_concurrent_by_ip",
"history_table_activity", "plexpy_auto_update", "history_table_activity", "plexpy_auto_update",
"themoviedb_lookup", "tvmaze_lookup" "themoviedb_lookup", "tvmaze_lookup", "http_plex_admin"
] ]
for checked_config in checked_configs: for checked_config in checked_configs:
if checked_config not in kwargs: if checked_config not in kwargs:
@ -2673,8 +2674,7 @@ class WebInterface(object):
refresh_users = False refresh_users = False
# First run from the setup wizard # First run from the setup wizard
if kwargs.get('first_run'): if kwargs.pop('first_run', None):
del kwargs['first_run']
first_run = True first_run = True
# If we change any monitoring settings, make sure we reschedule tasks. # If we change any monitoring settings, make sure we reschedule tasks.
@ -2728,12 +2728,15 @@ class WebInterface(object):
refresh_libraries = True refresh_libraries = True
# If we change the server, make sure we grab the new url and refresh libraries and users lists. # If we change the server, make sure we grab the new url and refresh libraries and users lists.
if kwargs.get('server_changed'): if kwargs.pop('server_changed', None):
del kwargs['server_changed']
server_changed = True server_changed = True
refresh_users = True refresh_users = True
refresh_libraries = True refresh_libraries = True
# If we change the authentication settings, make sure we refresh the users lists.
if kwargs.pop('auth_changed', None):
refresh_users = True
plexpy.CONFIG.process_kwargs(kwargs) plexpy.CONFIG.process_kwargs(kwargs)
# Write the config # Write the config

View file

@ -68,8 +68,12 @@ def initialize(options):
protocol = "http" protocol = "http"
if options['http_password']: if options['http_password']:
logger.info(u"Tautulli WebStart :: Web server authentication is enabled, username is '%s'", login_allowed = ["Tautulli admin (username is '%s')" % options['http_username']]
options['http_username']) if plexpy.CONFIG.HTTP_PLEX_ADMIN:
login_allowed.append("Plex admin")
logger.info(u"Tautulli WebStart :: Web server authentication is enabled: %s allowed", ' and '.join(login_allowed))
if options['http_basic_auth']: if options['http_basic_auth']:
auth_enabled = False auth_enabled = False
basic_auth_enabled = True basic_auth_enabled = True