+ {% endif %}
diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst
index 54e756ce..9d7262e6 100644
--- a/docs/source/about/usage.rst
+++ b/docs/source/about/usage.rst
@@ -298,6 +298,24 @@ Description
Default
``9494``
+Web UI Require Login
+^^^^^^^^^^^^^^^^^^^^
+
+Description
+ If set to ``True``, the Web UI will require the user to log in with their Plex account before accessing the Web UI.
+ Only the owner of the Plex Media Server will be able to log in.
+
+.. danger::
+ Disabling this value will allow anyone with network access to the Web UI to access it.
+
+ For security reasons, this value is not configurable via the Web UI. You need to manually edit the plug-in's XML
+ preferences file. If you do not know how to do this, you should not disable this value.
+
+ Changing this value requires a Plex Media Server restart.
+
+Default
+ ``True``
+
Log all web server messages
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 126d00f5..db5ce39c 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,6 @@
# development environment requirements, these should not be distributed
flake8==3.9.2;python_version<"3"
+mock==3.0.5;python_version<"3"
m2r2==0.3.2;python_version<"3"
numpydoc==0.9.2;python_version<"3"
plexhints==2023.1211.160853 # type hinting library for plex development
diff --git a/tests/conftest.py b/tests/conftest.py
index 6bd28a15..d2e7b982 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -5,15 +5,21 @@
import os
import sys
import time
+from mock import patch
# lib imports
import plexapi
from plexapi.exceptions import NotFound
from plexapi.server import PlexServer
from plexhints.agent_kit import Agent
+from plexhints.prefs_kit import _PreferenceSet
import pytest
import requests
+# create a fake directory for plexhints
+if not os.path.exists('plexhints'):
+ os.mkdir('plexhints')
+
# add Contents directory to the system path
pytest.root_dir = root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
pytest.contents_dir = contents_dir = os.path.join(root_dir, 'Contents')
@@ -87,6 +93,18 @@ def agent(request):
return Themerr()
+# extend the _PreferenceSet class, and give it a setter
+class PreferenceSet(_PreferenceSet):
+ def __setitem__(self, key, value):
+ self.update_user_values(**{key: value})
+
+
+@pytest.fixture(scope='function')
+def prefs_webapp_patch():
+ with patch('Code.webapp.Prefs', new_callable=lambda: PreferenceSet('test_webapp_prefs')) as mock_prefs:
+ yield mock_prefs
+
+
@pytest.fixture(scope='function')
def test_client():
"""Create a test client for testing webapp endpoints"""
@@ -102,6 +120,13 @@ def test_client():
yield test_client # this is where the testing happens!
+@pytest.fixture(scope='function')
+def test_client_login_disabled(test_client, prefs_webapp_patch):
+ """Create a test client for testing webapp endpoints with login disabled"""
+ webapp.Prefs['bool_webapp_require_login'] = False # disable login requirement
+ yield test_client
+
+
# plex server fixtures
@pytest.fixture(scope="session")
def plugin_logs():
@@ -111,7 +136,6 @@ def plugin_logs():
yield plugin_logs
-# plex server fixtures
@pytest.fixture(scope="session")
def plugin_log_file():
# the primary plugin log file
@@ -136,6 +160,11 @@ def plex(request, sess):
return PlexServer(SERVER_BASEURL, SERVER_TOKEN, session=sess)
+@pytest.fixture(scope="session")
+def plex_token():
+ return SERVER_TOKEN
+
+
@pytest.fixture(params=MOVIE_SECTIONS, scope="session")
def movie_section_names(plex, request):
library_section = request.param
diff --git a/tests/functional/test_webapp.py b/tests/functional/test_webapp.py
index 03087201..2fca653e 100644
--- a/tests/functional/test_webapp.py
+++ b/tests/functional/test_webapp.py
@@ -15,14 +15,16 @@ def remove_themerr_db_cache_file():
_backup_file_name = "{}.bak".format(webapp.database_cache_file)
# rename the file, so it is not found
- os.rename(webapp.database_cache_file, _backup_file_name)
+ if os.path.exists(webapp.database_cache_file):
+ os.rename(webapp.database_cache_file, _backup_file_name)
yield
# rename the file back
- os.rename(_backup_file_name, webapp.database_cache_file)
+ if os.path.exists(_backup_file_name):
+ os.rename(_backup_file_name, webapp.database_cache_file)
-def test_home(test_client):
+def test_home_login_disabled(test_client_login_disabled):
"""
WHEN the '/' page is requested (GET)
THEN check that the response is valid
@@ -30,49 +32,98 @@ def test_home(test_client):
Repeat for '/home'
"""
try:
- response = test_client.get('/')
+ response = test_client_login_disabled.get('/')
except AttributeError:
pytest.skip("cannot access Plex token/server")
else:
assert response.status_code == 200
- response = test_client.get('/home')
+ response = test_client_login_disabled.get('/home')
assert response.status_code == 200
assert 'id="section_' in response.data.decode('utf-8')
-def test_home_without_cache(remove_themerr_db_cache_file, test_client):
+def test_home(test_client):
"""
WHEN the '/' page is requested (GET)
- THEN check that the response is valid
+ THEN check that the response is a redirect
+
+ Repeat for '/home'
"""
try:
response = test_client.get('/')
except AttributeError:
pytest.skip("cannot access Plex token/server")
+ else:
+ assert response.status_code == 302
+
+ response = test_client.get('/home')
+ assert response.status_code == 302
+
+
+def test_home_without_cache_login_disabled(remove_themerr_db_cache_file, test_client_login_disabled):
+ """
+ WHEN the '/' page is requested (GET)
+ THEN check that the response is valid
+ """
+ try:
+ response = test_client_login_disabled.get('/')
+ except AttributeError:
+ pytest.skip("cannot access Plex token/server")
else:
assert response.status_code == 200
assert 'Database is being cached' in response.data.decode('utf-8')
-def test_image(test_client):
+def test_home_without_cache(remove_themerr_db_cache_file, test_client):
+ """
+ WHEN the '/' page is requested (GET)
+ THEN check that the response is a redirect
+ """
+ try:
+ response = test_client.get('/')
+ except AttributeError:
+ pytest.skip("cannot access Plex token/server")
+ else:
+ assert response.status_code == 302
+
+
+def test_image_login_disabled(test_client_login_disabled):
"""
WHEN the '/favicon.ico' file is requested (GET)
THEN check that the response is valid
THEN check the content type is 'image/vnd.microsoft.icon'
"""
- response = test_client.get('favicon.ico')
+ response = test_client_login_disabled.get('favicon.ico')
assert response.status_code == 200
assert response.content_type == 'image/vnd.microsoft.icon'
-def test_status(test_client):
+def test_image(test_client):
+ """
+ WHEN the '/favicon.ico' file is requested (GET)
+ THEN check that the response is a redirect
+ """
+ response = test_client.get('favicon.ico')
+ assert response.status_code == 302
+
+
+def test_status_no_login(test_client_login_disabled):
"""
WHEN the '/status' page is requested (GET)
THEN check that the response is valid
"""
- response = test_client.get('/status')
+ response = test_client_login_disabled.get('/status')
assert response.status_code == 200
assert response.content_type == 'application/json'
+
+
+def test_status(test_client):
+ """
+ WHEN the '/status' page is requested (GET)
+ THEN check that the response is a redirect
+ """
+ response = test_client.get('/status')
+ assert response.status_code == 302
diff --git a/tests/unit/test_plex_api_helper.py b/tests/unit/test_plex_api_helper.py
index f841e06a..79239258 100644
--- a/tests/unit/test_plex_api_helper.py
+++ b/tests/unit/test_plex_api_helper.py
@@ -24,3 +24,20 @@ def test_change_lock_status(section, lock):
change_status = plex_api_helper.change_lock_status(item, field=field, lock=lock)
assert change_status, 'change_lock_status did not return True'
assert item.isLocked(field=field) == lock, 'Failed to change lock status to {}'.format(lock)
+
+
+def test_get_user_info(plex_token):
+ assert plex_api_helper.get_user_info(token=plex_token)
+
+
+def test_get_user_info_invalid_token():
+ assert not plex_api_helper.get_user_info(token='invalid_token')
+
+
+def test_is_server_owner(plex_token):
+ user = plex_api_helper.get_user_info(token=plex_token)
+ assert plex_api_helper.is_server_owner(user=user)
+
+
+def test_is_not_server_owner():
+ assert plex_api_helper.is_server_owner(user={}) is None