From 737d758362aedd1646d925f39d4190404b56b700 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Tue, 7 May 2024 07:30:57 +0200 Subject: [PATCH 1/7] Return 404 if current element is not visible --- cps/opds.py | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/cps/opds.py b/cps/opds.py index b13b0570..c0ed6c20 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -31,7 +31,7 @@ from flask_babel import gettext as _ from sqlalchemy.sql.expression import func, text, or_, and_, true from sqlalchemy.exc import InvalidRequestError, OperationalError -from . import logger, config, db, calibre_db, ub, isoLanguages +from . import logger, config, db, calibre_db, ub, isoLanguages, constants from .usermanagement import requires_basic_auth_if_no_ano from .helper import get_download_link, get_book_cover from .pagination import Pagination @@ -94,6 +94,8 @@ def feed_letter_books(book_id): @opds.route("/opds/new") @requires_basic_auth_if_no_ano def feed_new(): + if not current_user.check_visibility(constants.SIDEBAR_RECENT): + abort(404) off = request.args.get("offset") or 0 entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, True, [db.Books.timestamp.desc()], @@ -104,6 +106,8 @@ def feed_new(): @opds.route("/opds/discover") @requires_basic_auth_if_no_ano def feed_discover(): + if not current_user.check_visibility(constants.SIDEBAR_RANDOM): + abort(404) query = calibre_db.generate_linked_query(config.config_read_column, db.Books) entries = query.filter(calibre_db.common_filters()).order_by(func.random()).limit(config.config_books_per_page) pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page)) @@ -113,6 +117,8 @@ def feed_discover(): @opds.route("/opds/rated") @requires_basic_auth_if_no_ano def feed_best_rated(): + if not current_user.check_visibility(constants.SIDEBAR_RATED): + abort(404) off = request.args.get("offset") or 0 entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.ratings.any(db.Ratings.rating > 9), @@ -124,6 +130,8 @@ def feed_best_rated(): @opds.route("/opds/hot") @requires_basic_auth_if_no_ano def feed_hot(): + if not current_user.check_visibility(constants.SIDEBAR_DOWNLOAD): + abort(404) off = request.args.get("offset") or 0 all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)).order_by( func.count(ub.Downloads.book_id).desc()).group_by(ub.Downloads.book_id) @@ -146,12 +154,16 @@ def feed_hot(): @opds.route("/opds/author") @requires_basic_auth_if_no_ano def feed_authorindex(): + if not current_user.check_visibility(constants.SIDEBAR_AUTHOR): + abort(404) return render_element_index(db.Authors.sort, db.books_authors_link, 'opds.feed_letter_author') @opds.route("/opds/author/letter/") @requires_basic_auth_if_no_ano def feed_letter_author(book_id): + if not current_user.check_visibility(constants.SIDEBAR_AUTHOR): + abort(404) off = request.args.get("offset") or 0 letter = true() if book_id == "00" else func.upper(db.Authors.sort).startswith(book_id) entries = calibre_db.session.query(db.Authors).join(db.books_authors_link).join(db.Books)\ @@ -173,6 +185,8 @@ def feed_author(book_id): @opds.route("/opds/publisher") @requires_basic_auth_if_no_ano def feed_publisherindex(): + if not current_user.check_visibility(constants.SIDEBAR_PUBLISHER): + abort(404) off = request.args.get("offset") or 0 entries = calibre_db.session.query(db.Publishers)\ .join(db.books_publishers_link)\ @@ -194,12 +208,16 @@ def feed_publisher(book_id): @opds.route("/opds/category") @requires_basic_auth_if_no_ano def feed_categoryindex(): + if not current_user.check_visibility(constants.SIDEBAR_CATEGORY): + abort(404) return render_element_index(db.Tags.name, db.books_tags_link, 'opds.feed_letter_category') @opds.route("/opds/category/letter/") @requires_basic_auth_if_no_ano def feed_letter_category(book_id): + if not current_user.check_visibility(constants.SIDEBAR_CATEGORY): + abort(404) off = request.args.get("offset") or 0 letter = true() if book_id == "00" else func.upper(db.Tags.name).startswith(book_id) entries = calibre_db.session.query(db.Tags)\ @@ -223,12 +241,16 @@ def feed_category(book_id): @opds.route("/opds/series") @requires_basic_auth_if_no_ano def feed_seriesindex(): + if not current_user.check_visibility(constants.SIDEBAR_SERIES): + abort(404) return render_element_index(db.Series.sort, db.books_series_link, 'opds.feed_letter_series') @opds.route("/opds/series/letter/") @requires_basic_auth_if_no_ano def feed_letter_series(book_id): + if not current_user.check_visibility(constants.SIDEBAR_SERIES): + abort(404) off = request.args.get("offset") or 0 letter = true() if book_id == "00" else func.upper(db.Series.sort).startswith(book_id) entries = calibre_db.session.query(db.Series)\ @@ -258,6 +280,8 @@ def feed_series(book_id): @opds.route("/opds/ratings") @requires_basic_auth_if_no_ano def feed_ratingindex(): + if not current_user.check_visibility(constants.SIDEBAR_RATING): + abort(404) off = request.args.get("offset") or 0 entries = calibre_db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), (db.Ratings.rating / 2).label('name')) \ @@ -284,6 +308,8 @@ def feed_ratings(book_id): @opds.route("/opds/formats") @requires_basic_auth_if_no_ano def feed_formatindex(): + if not current_user.check_visibility(constants.SIDEBAR_FORMAT): + abort(404) off = request.args.get("offset") or 0 entries = calibre_db.session.query(db.Data).join(db.Books)\ .filter(calibre_db.common_filters()) \ @@ -291,7 +317,6 @@ def feed_formatindex(): .order_by(db.Data.format).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(entries)) - element = list() for entry in entries: element.append(FeedObject(entry.format, entry.format)) @@ -314,6 +339,8 @@ def feed_format(book_id): @opds.route("/opds/language/") @requires_basic_auth_if_no_ano def feed_languagesindex(): + if not current_user.check_visibility(constants.SIDEBAR_LANGUAGE): + abort(404) off = request.args.get("offset") or 0 if current_user.filter_language() == "all": languages = calibre_db.speaking_language() @@ -341,6 +368,8 @@ def feed_languages(book_id): @opds.route("/opds/shelfindex") @requires_basic_auth_if_no_ano def feed_shelfindex(): + if not (current_user.is_authenticated or g.allow_anonymous): + abort(404) off = request.args.get("offset") or 0 shelf = ub.session.query(ub.Shelf).filter( or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all() @@ -353,7 +382,8 @@ def feed_shelfindex(): @opds.route("/opds/shelf/") @requires_basic_auth_if_no_ano def feed_shelf(book_id): - off = request.args.get("offset") or 0 + if not (current_user.is_authenticated or g.allow_anonymous): + abort(404) if current_user.is_anonymous: shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1, ub.Shelf.id == book_id).first() @@ -436,6 +466,8 @@ def feed_get_cover(book_id): @opds.route("/opds/readbooks") @requires_basic_auth_if_no_ano def feed_read_books(): + if not (current_user.check_visibility(constants.SIDEBAR_READ_AND_UNREAD) and not current_user.is_anonymous): + return abort(403) off = request.args.get("offset") or 0 result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True) return render_xml_template('feed.xml', entries=result, pagination=pagination) @@ -444,6 +476,8 @@ def feed_read_books(): @opds.route("/opds/unreadbooks") @requires_basic_auth_if_no_ano def feed_unread_books(): + if not (current_user.check_visibility(constants.SIDEBAR_READ_AND_UNREAD) and not current_user.is_anonymous): + return abort(403) off = request.args.get("offset") or 0 result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True) return render_xml_template('feed.xml', entries=result, pagination=pagination) @@ -477,7 +511,7 @@ def feed_search(term): def render_xml_template(*args, **kwargs): # ToDo: return time in current timezone similar to %z currtime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00") - xml = render_template(current_time=currtime, instance=config.config_calibre_web_title, *args, **kwargs) + xml = render_template(current_time=currtime, instance=config.config_calibre_web_title, constants=constants.sidebar_settings, *args, **kwargs) response = make_response(xml) response.headers["Content-Type"] = "application/atom+xml; charset=utf-8" return response From cb62d36e4479fe7b30ed015bc48232b8cf2318e3 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Tue, 7 May 2024 08:27:55 +0200 Subject: [PATCH 2/7] Bugfix for visibility opds feed --- cps/opds.py | 5 +++-- cps/templates/index.xml | 29 ++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/cps/opds.py b/cps/opds.py index c0ed6c20..cd77db35 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -24,7 +24,7 @@ import datetime import json from urllib.parse import unquote_plus -from flask import Blueprint, request, render_template, make_response, abort, Response +from flask import Blueprint, request, render_template, make_response, abort, Response, g from flask_login import current_user from flask_babel import get_locale from flask_babel import gettext as _ @@ -117,7 +117,7 @@ def feed_discover(): @opds.route("/opds/rated") @requires_basic_auth_if_no_ano def feed_best_rated(): - if not current_user.check_visibility(constants.SIDEBAR_RATED): + if not current_user.check_visibility(constants.SIDEBAR_BEST_RATED): abort(404) off = request.args.get("offset") or 0 entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, @@ -384,6 +384,7 @@ def feed_shelfindex(): def feed_shelf(book_id): if not (current_user.is_authenticated or g.allow_anonymous): abort(404) + off = request.args.get("offset") or 0 if current_user.is_anonymous: shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1, ub.Shelf.id == book_id).first() diff --git a/cps/templates/index.xml b/cps/templates/index.xml index cae3f629..8d4cfadb 100644 --- a/cps/templates/index.xml +++ b/cps/templates/index.xml @@ -22,6 +22,7 @@ {{ current_time }} {{_('Books sorted alphabetically')}} + {% if current_user.check_visibility(g.constants.SIDEBAR_DOWNLOAD) and (not current_user.is_anonymous) %} {{_('Hot Books')}} @@ -29,6 +30,8 @@ {{ current_time }} {{_('Popular publications from this catalog based on Downloads.')}} + {%endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_BEST_RATED) %} {{_('Top Rated Books')}} @@ -36,6 +39,8 @@ {{ current_time }} {{_('Popular publications from this catalog based on Rating.')}} + {%endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_RECENT) %} {{_('Recently added Books')}} @@ -43,6 +48,8 @@ {{ current_time }} {{_('The latest Books')}} + {%endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_RANDOM) %} {{_('Random Books')}} @@ -50,7 +57,8 @@ {{ current_time }} {{_('Show Random Books')}} - {% if not current_user.is_anonymous %} + {%endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_READ_AND_UNREAD) and not current_user.is_anonymous %} {{_('Read Books')}} @@ -66,6 +74,7 @@ {{_('Unread Books')}} {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_AUTHOR) %} {{_('Authors')}} @@ -73,13 +82,17 @@ {{ current_time }} {{_('Books ordered by Author')}} - + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_PUBLISHER) %} + {{_('Publishers')}} {{url_for('opds.feed_publisherindex')}} {{ current_time }} {{_('Books ordered by publisher')}} + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_CATEGORY) %} {{_('Categories')}} @@ -87,6 +100,8 @@ {{ current_time }} {{_('Books ordered by category')}} + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_SERIES) %} {{_('Series')}} @@ -94,6 +109,8 @@ {{ current_time }} {{_('Books ordered by series')}} + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_LANGUAGE) %} {{_('Languages')}} @@ -101,6 +118,8 @@ {{ current_time }} {{_('Books ordered by Languages')}} + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_RATING) %} {{_('Ratings')}} @@ -108,7 +127,8 @@ {{ current_time }} {{_('Books ordered by Rating')}} - + {% endif %} + {% if current_user.check_visibility(g.constants.SIDEBAR_FORMAT) %} {{_('File formats')}} @@ -116,6 +136,8 @@ {{ current_time }} {{_('Books ordered by file formats')}} + {% endif %} + {% if current_user.is_authenticated or g.allow_anonymous %} {{_('Shelves')}} @@ -123,4 +145,5 @@ {{ current_time }} {{_('Books organized in shelves')}} + {% endif %} From 60ed1904f59d8e42f3c4d69e73bf6d51562333b7 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Fri, 10 May 2024 09:04:50 +0200 Subject: [PATCH 3/7] testrun --- setup.cfg | 1 + test/Calibre-Web TestSummary_Linux.html | 1215 +++++++++++++++-------- 2 files changed, 776 insertions(+), 440 deletions(-) diff --git a/setup.cfg b/setup.cfg index 615a44da..8048eea5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,6 +58,7 @@ install_requires = chardet>=3.0.0,<4.1.0 advocate>=1.0.0,<1.1.0 Flask-Limiter>=2.3.0,<3.6.0 + regex>=2022.3.2,<2024.2.25 [options.packages.find] diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index 196ea131..59d37113 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
-

Start Time: 2024-02-26 20:07:24

+

Start Time: 2024-05-07 08:28:49

-

Stop Time: 2024-02-27 03:19:17

+

Stop Time: 2024-05-07 15:28:53

-

Duration: 6h 0 min

+

Duration: 5h 48 min

@@ -234,12 +234,12 @@ - + TestBackupMetadata 21 20 - 1 0 + 1 0 Detail @@ -320,38 +320,26 @@ - +
TestBackupMetadata - test_backup_change_book_series_index
- PASS - - - - - - -
TestBackupMetadata - test_backup_change_book_tags
-
- FAIL + ERROR
-
Traceback (most recent call last):
-  File "/home/ozzie/Development/calibre-web-test/test/test_login.py", line 532, in test_proxy_login_multi_user
+  File "/home/ozzie/Development/calibre-web-test/test/test_login.py", line 575, in test_proxy_login_multi_user
     self.assertTrue('<input type="text" class="form-control" name="name" id="name" value="new_user1" autocomplete="off">' in resp.text)
 AssertionError: False is not true
@@ -3532,12 +3872,12 @@ AssertionError: False is not true - + TestOPDSFeed 24 - 24 - 0 - 0 + 22 + 1 + 1 0 Detail @@ -3618,11 +3958,31 @@ AssertionError: False is not true - +
TestOPDSFeed - test_opds_guest_user
- PASS + +
+ ERROR +
+ + + + @@ -3690,11 +4050,31 @@ AssertionError: False is not true - +
TestOPDSFeed - test_opds_search
- PASS + +
+ FAIL +
+ + + + @@ -3763,6 +4143,94 @@ AssertionError: False is not true + + _ErrorHolder + 2 + 0 + 0 + 2 + 0 + + Detail + + + + + + + +
tearDownClass (test_opds_feed)
+ + +
+ ERROR +
+ + + + + + + + + + +
setUpClass (test_zz_helper)
+ + +
+ ERROR +
+ + + + + + + + + TestUploadPDF 1 @@ -3771,13 +4239,13 @@ AssertionError: False is not true 0 0 - Detail + Detail - +
TestUploadPDF - test_upload_invalid_pdf
@@ -3795,13 +4263,13 @@ AssertionError: False is not true 0 0 - Detail + Detail - +
TestPipInstall - test_command_start
@@ -3810,7 +4278,7 @@ AssertionError: False is not true - +
TestPipInstall - test_foldername_database_location
@@ -3819,7 +4287,7 @@ AssertionError: False is not true - +
TestPipInstall - test_module_start
@@ -3837,13 +4305,13 @@ AssertionError: False is not true 0 1 - Detail + Detail - +
TestReader - test_cb7_reader
@@ -3852,7 +4320,7 @@ AssertionError: False is not true - +
TestReader - test_comic_MACOS_files
@@ -3861,7 +4329,7 @@ AssertionError: False is not true - +
TestReader - test_comic_reader
@@ -3870,7 +4338,7 @@ AssertionError: False is not true - +
TestReader - test_epub_reader
@@ -3879,7 +4347,7 @@ AssertionError: False is not true - +
TestReader - test_pdf_reader
@@ -3888,7 +4356,7 @@ AssertionError: False is not true - +
TestReader - test_single_file_comic
@@ -3897,7 +4365,7 @@ AssertionError: False is not true - +
TestReader - test_sound_listener
@@ -3906,7 +4374,7 @@ AssertionError: False is not true - +
TestReader - test_txt_reader
@@ -3924,13 +4392,13 @@ AssertionError: False is not true 0 0 - Detail + Detail - +
TestReadOnlyDatabase - test_readonly_path
@@ -3948,13 +4416,13 @@ AssertionError: False is not true 0 0 - Detail + Detail - +
TestRegister - test_forgot_password
@@ -3963,7 +4431,7 @@ AssertionError: False is not true - +
TestRegister - test_illegal_email
@@ -3972,7 +4440,7 @@ AssertionError: False is not true - +
TestRegister - test_limit_domain
@@ -3981,7 +4449,7 @@ AssertionError: False is not true - +
TestRegister - test_register_no_server
@@ -3990,7 +4458,7 @@ AssertionError: False is not true - +
TestRegister - test_registering_only_email
@@ -3999,7 +4467,7 @@ AssertionError: False is not true - +
TestRegister - test_registering_user
@@ -4008,7 +4476,7 @@ AssertionError: False is not true - +
TestRegister - test_registering_user_fail
@@ -4017,7 +4485,7 @@ AssertionError: False is not true - +
TestRegister - test_user_change_password
@@ -4035,13 +4503,13 @@ AssertionError: False is not true 0 0 - Detail + Detail - +
TestReverseProxy - test_logout
@@ -4050,7 +4518,7 @@ AssertionError: False is not true - +
TestReverseProxy - test_move_page
@@ -4059,7 +4527,7 @@ AssertionError: False is not true - +
TestReverseProxy - test_next
@@ -4068,7 +4536,7 @@ AssertionError: False is not true - +
TestReverseProxy - test_reverse_about
@@ -4086,13 +4554,13 @@ AssertionError: False is not true 0 1 - Detail + Detail - +
TestShelf - test_access_shelf
@@ -4101,7 +4569,7 @@ AssertionError: False is not true - +
TestShelf - test_add_shelf_from_search
@@ -4110,7 +4578,7 @@ AssertionError: False is not true - +
TestShelf - test_adv_search_shelf
@@ -4119,7 +4587,7 @@ AssertionError: False is not true - +
TestShelf - test_arrange_shelf
@@ -4128,7 +4596,7 @@ AssertionError: False is not true - +
TestShelf - test_create_public_shelf
@@ -4137,7 +4605,7 @@ AssertionError: False is not true - +
TestShelf - test_create_public_shelf_no_permission
@@ -4146,7 +4614,7 @@ AssertionError: False is not true - +
TestShelf - test_delete_book_of_shelf
@@ -4155,7 +4623,7 @@ AssertionError: False is not true - +
TestShelf - test_private_shelf
@@ -4164,7 +4632,7 @@ AssertionError: False is not true - +
TestShelf - test_public_private_shelf
@@ -4173,7 +4641,7 @@ AssertionError: False is not true - +
TestShelf - test_public_shelf
@@ -4182,7 +4650,7 @@ AssertionError: False is not true - +
TestShelf - test_rename_shelf
@@ -4191,7 +4659,7 @@ AssertionError: False is not true - +
TestShelf - test_shelf_action_non_shelf_edit_role
@@ -4200,7 +4668,7 @@ AssertionError: False is not true - +
TestShelf - test_shelf_anonymous
@@ -4209,19 +4677,19 @@ AssertionError: False is not true - +
TestShelf - test_shelf_database_change
- SKIP + SKIP
-