From 7ec5e88bc46389b05743744c77c3a601f5c68390 Mon Sep 17 00:00:00 2001 From: krateng Date: Mon, 18 Dec 2023 03:22:54 +0100 Subject: [PATCH 1/2] Upgrade auth and logging to new doreah --- dev/releases/3.2.yml | 6 ++++- maloja/apis/native_v1.py | 45 ++++++++++++++++++------------------- maloja/database/__init__.py | 1 - maloja/dev/profiler.py | 7 +++--- maloja/pkg_global/conf.py | 26 ++++++++------------- maloja/server.py | 9 ++++---- maloja/setup.py | 11 +++++---- pyproject.toml | 2 +- requirements.txt | 2 +- 9 files changed, 50 insertions(+), 59 deletions(-) diff --git a/dev/releases/3.2.yml b/dev/releases/3.2.yml index 3c890e6..7d9f2a1 100644 --- a/dev/releases/3.2.yml +++ b/dev/releases/3.2.yml @@ -32,8 +32,12 @@ minor_release_name: "Nicole" - "[Bugfix] Fixed Spotify authentication thread blocking the process from terminating" - "[Technical] Upgraded all third party modules to use requests module and send User Agent" 3.2.2: + commit: "febaff97228b37a192f2630aa331cac5e5c3e98e" notes: - "[Security] Fixed XSS vulnerability in error page (Disclosed by https://github.com/NULLYUKI)" - "[Architecture] Reworked the default directory selection" - "[Feature] Added option to show scrobbles on tile charts" - - "[Bugfix] Fixed Last.fm authentication" \ No newline at end of file + - "[Bugfix] Fixed Last.fm authentication" +3.2.3: + notes: + - "[Architecture] Upgraded doreah, significant rework of authentication" \ No newline at end of file diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index aea7710..1c9ad80 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -7,7 +7,6 @@ from bottle import response, static_file, FormsDict from inspect import signature from doreah.logging import log -from doreah.auth import authenticated_function # nimrodel API from nimrodel import EAPI as API @@ -15,7 +14,7 @@ from nimrodel import Multi from .. import database -from ..pkg_global.conf import malojaconfig, data_dir +from ..pkg_global.conf import malojaconfig, data_dir, auth @@ -567,7 +566,7 @@ def album_info_external(k_filter, k_limit, k_delimit, k_amount): @api.post("newscrobble") -@authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result') +@auth.authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result') @catch_exceptions def post_scrobble( artist:Multi=None, @@ -647,7 +646,7 @@ def post_scrobble( @api.post("addpicture") -@authenticated_function(alternate=api_key_correct,api=True) +@auth.authenticated_function(alternate=api_key_correct,api=True) @catch_exceptions @convert_kwargs def add_picture(k_filter, k_limit, k_delimit, k_amount, k_special): @@ -670,7 +669,7 @@ def add_picture(k_filter, k_limit, k_delimit, k_amount, k_special): @api.post("importrules") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def import_rulemodule(**keys): """Internal Use Only""" @@ -689,7 +688,7 @@ def import_rulemodule(**keys): @api.post("rebuild") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def rebuild(**keys): """Internal Use Only""" @@ -765,7 +764,7 @@ def search(**keys): @api.post("newrule") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def newrule(**keys): """Internal Use Only""" @@ -776,21 +775,21 @@ def newrule(**keys): @api.post("settings") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def set_settings(**keys): """Internal Use Only""" malojaconfig.update(keys) @api.post("apikeys") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def set_apikeys(**keys): """Internal Use Only""" apikeystore.update(keys) @api.post("import") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def import_scrobbles(identifier): """Internal Use Only""" @@ -798,7 +797,7 @@ def import_scrobbles(identifier): import_scrobbles(identifier) @api.get("backup") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def get_backup(**keys): """Internal Use Only""" @@ -811,7 +810,7 @@ def get_backup(**keys): return static_file(os.path.basename(archivefile),root=tmpfolder) @api.get("export") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def get_export(**keys): """Internal Use Only""" @@ -825,7 +824,7 @@ def get_export(**keys): @api.post("delete_scrobble") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def delete_scrobble(timestamp): """Internal Use Only""" @@ -837,7 +836,7 @@ def delete_scrobble(timestamp): @api.post("edit_artist") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def edit_artist(id,name): """Internal Use Only""" @@ -847,7 +846,7 @@ def edit_artist(id,name): } @api.post("edit_track") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def edit_track(id,title): """Internal Use Only""" @@ -857,7 +856,7 @@ def edit_track(id,title): } @api.post("edit_album") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def edit_album(id,albumtitle): """Internal Use Only""" @@ -868,7 +867,7 @@ def edit_album(id,albumtitle): @api.post("merge_tracks") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def merge_tracks(target_id,source_ids): """Internal Use Only""" @@ -879,7 +878,7 @@ def merge_tracks(target_id,source_ids): } @api.post("merge_artists") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def merge_artists(target_id,source_ids): """Internal Use Only""" @@ -890,7 +889,7 @@ def merge_artists(target_id,source_ids): } @api.post("merge_albums") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def merge_artists(target_id,source_ids): """Internal Use Only""" @@ -901,7 +900,7 @@ def merge_artists(target_id,source_ids): } @api.post("associate_albums_to_artist") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def associate_albums_to_artist(target_id,source_ids,remove=False): result = database.associate_albums_to_artist(target_id,source_ids,remove=remove) @@ -913,7 +912,7 @@ def associate_albums_to_artist(target_id,source_ids,remove=False): } @api.post("associate_tracks_to_artist") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def associate_tracks_to_artist(target_id,source_ids,remove=False): result = database.associate_tracks_to_artist(target_id,source_ids,remove=remove) @@ -925,7 +924,7 @@ def associate_tracks_to_artist(target_id,source_ids,remove=False): } @api.post("associate_tracks_to_album") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def associate_tracks_to_album(target_id,source_ids): result = database.associate_tracks_to_album(target_id,source_ids) @@ -937,7 +936,7 @@ def associate_tracks_to_album(target_id,source_ids): @api.post("reparse_scrobble") -@authenticated_function(api=True) +@auth.authenticated_function(api=True) @catch_exceptions def reparse_scrobble(timestamp): """Internal Use Only""" diff --git a/maloja/database/__init__.py b/maloja/database/__init__.py index fe5ce62..97634db 100644 --- a/maloja/database/__init__.py +++ b/maloja/database/__init__.py @@ -27,7 +27,6 @@ from . import exceptions # doreah toolkit from doreah.logging import log -from doreah.auth import authenticated_api, authenticated_api_with_alternate import doreah diff --git a/maloja/dev/profiler.py b/maloja/dev/profiler.py index c20db90..49563a4 100644 --- a/maloja/dev/profiler.py +++ b/maloja/dev/profiler.py @@ -1,9 +1,9 @@ import os import cProfile, pstats +import time from doreah.logging import log -from doreah.timing import Clock from ..pkg_global.conf import data_dir @@ -27,8 +27,7 @@ def profile(func): def newfunc(*args,**kwargs): - clock = Clock() - clock.start() + starttime = time.time() if FULL_PROFILE: benchmarkfolder = data_dir['logs']("benchmarks") @@ -44,7 +43,7 @@ def profile(func): if FULL_PROFILE: localprofiler.disable() - seconds = clock.stop() + seconds = time.time() - starttime if not SINGLE_CALLS: times.setdefault(realfunc,[]).append(seconds) diff --git a/maloja/pkg_global/conf.py b/maloja/pkg_global/conf.py index 26ff692..eff0746 100644 --- a/maloja/pkg_global/conf.py +++ b/maloja/pkg_global/conf.py @@ -1,4 +1,7 @@ import os + +import doreah.auth +import doreah.logging from doreah.configuration import Configuration from doreah.configuration import types as tp @@ -331,26 +334,15 @@ data_dir = { -### DOREAH CONFIGURATION +### DOREAH OBJECTS -from doreah import config - -config( - auth={ - "multiuser":False, - "cookieprefix":"maloja", - "stylesheets":["/maloja.css"], - "dbfile":data_dir['auth']("auth.ddb") - }, - logging={ - "logfolder": data_dir['logs']() if malojaconfig["LOGGING"] else None - }, - regular={ - "offset": malojaconfig["TIMEZONE"] - } -) +auth = doreah.auth.AuthManager(singleuser=True,cookieprefix='maloja',stylesheets=("/maloja.css",),dbfile=data_dir['auth']("auth.sqlite")) +#logger = doreah.logging.Logger(logfolder=data_dir['logs']() if malojaconfig["LOGGING"] else None) +#log = logger.log +# this is not how its supposed to be done, but lets ease the transition +doreah.logging.defaultlogger.logfolder = data_dir['logs']() if malojaconfig["LOGGING"] else None diff --git a/maloja/server.py b/maloja/server.py index a2ff5bc..9090630 100644 --- a/maloja/server.py +++ b/maloja/server.py @@ -12,14 +12,13 @@ from jinja2.exceptions import TemplateNotFound # doreah toolkit from doreah.logging import log -from doreah import auth # rest of the project from . import database from .database.jinjaview import JinjaDBConnection from .images import image_request from .malojauri import uri_to_internal, remove_identical -from .pkg_global.conf import malojaconfig, data_dir +from .pkg_global.conf import malojaconfig, data_dir, auth from .pkg_global import conf from .jinjaenv.context import jinja_environment from .apis import init_apis, apikeystore @@ -97,7 +96,7 @@ aliases = { ### API -auth.authapi.mount(server=webserver) +conf.auth.authapi.mount(server=webserver) init_apis(webserver) # redirects for backwards compatibility @@ -197,7 +196,7 @@ def jinja_page(name): if name in aliases: redirect(aliases[name]) keys = remove_identical(FormsDict.decode(request.query)) - adminmode = request.cookies.get("adminmode") == "true" and auth.check(request) + adminmode = request.cookies.get("adminmode") == "true" and auth.check_request(request) with JinjaDBConnection() as conn: @@ -222,7 +221,7 @@ def jinja_page(name): return res @webserver.route("/") -@auth.authenticated +@auth.authenticated_function() def jinja_page_private(name): return jinja_page(name) diff --git a/maloja/setup.py b/maloja/setup.py index 4aa75dd..a8652f1 100644 --- a/maloja/setup.py +++ b/maloja/setup.py @@ -6,9 +6,8 @@ try: except ImportError: import distutils from doreah.io import col, ask, prompt -from doreah import auth -from .pkg_global.conf import data_dir, dir_settings, malojaconfig +from .pkg_global.conf import data_dir, dir_settings, malojaconfig, auth @@ -67,10 +66,10 @@ def setup(): if forcepassword is not None: # user has specified to force the pw, nothing else matters - auth.defaultuser.setpw(forcepassword) + auth.change_pw(password=forcepassword) print("Password has been set.") - elif auth.defaultuser.checkpw("admin"): - # if the actual pw is admin, it means we've never set this up properly (eg first start after update) + elif auth.still_has_factory_default_user(): + # this means we've never set this up properly (eg first start after update) while True: newpw = prompt("Please set a password for web backend access. Leave this empty to generate a random password.",skip=SKIP,secret=True) if newpw is None: @@ -81,7 +80,7 @@ def setup(): newpw_repeat = prompt("Please type again to confirm.",skip=SKIP,secret=True) if newpw != newpw_repeat: print("Passwords do not match!") else: break - auth.defaultuser.setpw(newpw) + auth.change_pw(password=newpw) except EOFError: print("No user input possible. If you are running inside a container, set the environment variable",col['yellow']("MALOJA_SKIP_SETUP=yes")) diff --git a/pyproject.toml b/pyproject.toml index 3a56757..883a911 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ dependencies = [ "bottle>=0.12.16", "waitress>=2.1.0", - "doreah>=1.9.4, <2", + "doreah>=2.0.0, <3", "nimrodel>=0.8.0", "setproctitle>=1.1.10", #"pyvips>=2.1.16", diff --git a/requirements.txt b/requirements.txt index 940b543..c869349 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ bottle>=0.12.16 waitress>=2.1.0 -doreah>=1.9.4, <2 +doreah>=2.0.0, <3 nimrodel>=0.8.0 setproctitle>=1.1.10 jinja2>=3.0.0 From b725c98fa521de2a86389f9c896dc1c393fefee5 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 28 Dec 2023 04:05:15 +0100 Subject: [PATCH 2/2] Bump requirements --- pyproject.toml | 6 ++++-- requirements.txt | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 883a911..774afdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "malojaserver" version = "3.2.2" description = "Self-hosted music scrobble database" readme = "./README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" license = { file="./LICENSE" } authors = [ { name="Johannes Krattenmacher", email="maloja@dev.krateng.ch" } ] @@ -31,7 +31,9 @@ dependencies = [ "sqlalchemy>=2.0", "python-datauri>=1.1.0", "requests>=2.27.1", - "setuptools>68.0.0" + "setuptools>68.0.0", + "toml>=0.10.2", + "PyYAML>=6.0.1" ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index c869349..c24f84b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,5 @@ sqlalchemy>=2.0 python-datauri>=1.1.0 requests>=2.27.1 setuptools>68.0.0 +toml>=0.10.2 +PyYAML>=6.0.1 \ No newline at end of file