Skip to content

Commit

Permalink
Merge pull request #1933 from NYPL-Simplified/TGR-35-circulation-mana…
Browse files Browse the repository at this point in the history
…ger-dependabot-alerts

TGR-35 Requirements Upgrades
  • Loading branch information
keithbauer authored Jul 22, 2024
2 parents 6a01164 + b41d937 commit ee17b76
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 54 deletions.
12 changes: 6 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,14 @@ CMD ["webapp"]
###############################################################################

FROM cm_webapp_base AS cm_webapp_local
ENV FLASK_ENV development
ENV FLASK_DEBUG 1

###############################################################################
## cm_webapp_active - self-contained version of webapp, for remote deploy
###############################################################################

FROM cm_webapp_base AS cm_webapp_active
ENV FLASK_ENV production
ENV FLASK_DEBUG 0

COPY --chown=simplified:simplified . /home/simplified/circulation/

Expand Down Expand Up @@ -263,14 +263,14 @@ CMD ["scripts", "|& tee -a /var/log/cron.log 2>$1"]
###############################################################################

FROM cm_scripts_base AS cm_scripts_local
ENV FLASK_ENV development
ENV FLASK_DEBUG 1

###############################################################################
## cm_scripts_active - self-contained version of scripts, for remote deploy
###############################################################################

FROM cm_scripts_base AS cm_scripts_active
ENV FLASK_ENV production
ENV FLASK_DEBUG 0

COPY --chown=simplified:simplified . /home/simplified/circulation/

Expand All @@ -286,13 +286,13 @@ CMD ["exec"]
###############################################################################

FROM cm_exec_base AS cm_exec_local
ENV FLASK_ENV development
ENV FLASK_DEBUG 1

###############################################################################
## cm_exec_active - self-contained version of exec, for remote deploy
###############################################################################

FROM cm_exec_base AS cm_exec_active
ENV FLASK_ENV production
ENV FLASK_DEBUG 0

COPY --chown=simplified:simplified . /home/simplified/circulation/
14 changes: 7 additions & 7 deletions api/admin/controller/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,16 +351,16 @@ def __call__(self, collection, book, path=None):
redirect_url = flask.request.url

if (collection):
quoted_collection = urllib.parse.quote(collection)
redirect_url = redirect_url.replace(
quoted_collection,
quoted_collection.replace("/", "%2F"))
collection,
urllib.parse.quote_plus(collection)
)

if (book):
quoted_book = urllib.parse.quote(book)
if (book):
redirect_url = redirect_url.replace(
quoted_book,
quoted_book.replace("/", "%2F"))
book,
urllib.parse.quote_plus(book)
)

return redirect(self.url_for('admin_sign_in', redirect=redirect_url))

Expand Down
9 changes: 7 additions & 2 deletions api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
Flask,
Response,
redirect,
request
)
from flask_swagger_ui import get_swaggerui_blueprint
from flask_sqlalchemy_session import flask_scoped_session
from .flask_sqlalchemy_session import flask_scoped_session
from .config import Configuration
from core.model import (
ConfigurationSetting,
Expand All @@ -37,12 +38,16 @@
from flask_babel import Babel


def get_locale():
languages = Configuration.localization_languages()
return request.accept_languages.best_match(languages, default="en")

app = Flask(__name__)
app._db = None
app.static_resources_dir = Configuration.static_resources_dir()
app.config['BABEL_DEFAULT_LOCALE'] = LanguageCodes.three_to_two[Configuration.localization_languages()[0]]
app.config['BABEL_TRANSLATION_DIRECTORIES'] = "../translations"
babel = Babel(app)
babel = Babel(app, locale_selector=get_locale)

swaggerui_print = get_swaggerui_blueprint(
'/apidocs_admin', '/admin_docs', blueprint_name='apmin_api'
Expand Down
8 changes: 4 additions & 4 deletions api/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.sql.expression import or_
from werkzeug.datastructures import Headers
from werkzeug.datastructures import auth, Headers

from api.util.short_client_token import ShortClientTokenUtility
from api.annotations import AnnotationWriter
Expand Down Expand Up @@ -834,7 +834,7 @@ def authenticated_patron(self, _db, header):

if (self.basic_auth_provider
and (
(isinstance(header, dict) and 'username' in header)
(isinstance(header, (dict, auth.Authorization)) and 'username' in header)
or provider_name == BasicAuthenticationProvider.BEARER_TOKEN_PROVIDER_NAME
)
):
Expand Down Expand Up @@ -1955,7 +1955,7 @@ def authenticate(self, _db, credentials):
if isinstance(credentials, str):
return self._authenticate_from_token(_db, credentials)

elif isinstance(credentials, dict):
elif isinstance(credentials, (dict, auth.Authorization)):
return self._authenticate_from_credentials(_db, credentials)

def _authenticate_from_token(self, _db, credentials):
Expand Down Expand Up @@ -2108,7 +2108,7 @@ def get_credential_from_header(self, header):
:param header: A dictionary with keys `username` and `password`.
"""
if not isinstance(header, dict):
if not isinstance(header, (dict, auth.Authorization)):
return None
return header.get('password', None)

Expand Down
60 changes: 60 additions & 0 deletions api/flask_sqlalchemy_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
https://github.com/dtheodor/flask-sqlalchemy-session is archived
Implements fixes to make it work with modern Werkzeug
https://github.com/dtheodor/flask-sqlalchemy-session/pull/17
https://github.com/dtheodor/flask-sqlalchemy-session/pull/15
"""

from flask import current_app
from sqlalchemy.orm import scoped_session
from werkzeug.local import LocalProxy


def _get_session():
app = current_app._get_current_object()
if not hasattr(app, "scoped_session"):
raise AttributeError(
"{} has no 'scoped_session' attribute. You need to initialize it "
"with a flask_scoped_session.".format(app)
)
return app.scoped_session


current_session = LocalProxy(_get_session)
"""Provides the current SQL Alchemy session within a request.
Will raise an exception if no :data:`~flask.current_app` is available or it has
not been initialized with a :class:`flask_scoped_session`
"""


class flask_scoped_session(scoped_session):
"""A :class:`~sqlalchemy.orm.scoping.scoped_session` whose scope is set to
the Flask application context.
"""

def __init__(self, session_factory, app=None):
"""
:param session_factory: A callable that returns a
:class:`~sqlalchemy.orm.session.Session`
:param app: a :class:`~flask.Flask` application
"""
try:
from greenlet import getcurrent as scopefunc
except ImportError:
from threading import get_ident as scopefunc
super().__init__(session_factory, scopefunc=scopefunc)
if app is not None:
self.init_app(app)

def init_app(self, app):
"""Setup scoped session creation and teardown for the passed ``app``.
:param app: a :class:`~flask.Flask` application
"""
app.scoped_session = self

@app.teardown_appcontext
def remove_scoped_session(*args, **kwargs):
app.scoped_session.remove()
6 changes: 1 addition & 5 deletions api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from flask_cors.core import get_cors_options, set_cors_headers
from werkzeug.exceptions import HTTPException

from .app import app, babel
from .app import app

# We use URIs as identifiers throughout the application, meaning that
# we never want werkzeug's merge_slashes feature.
Expand Down Expand Up @@ -57,10 +57,6 @@ def initialize_app_settings():
# Finds or generates a site-wide bearer token signing secret
BearerTokenSigner.bearer_token_signing_secret(app.manager._db)

@babel.localeselector
def get_locale():
languages = Configuration.localization_languages()
return request.accept_languages.best_match(languages)

@app.teardown_request
def shutdown_session(exception):
Expand Down
6 changes: 3 additions & 3 deletions core/tests/test_app_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def setup_method(self):
self.controller = ComplaintController()
self.edition, self.pool = self._edition(with_license_pool=True)
self.app = Flask(__name__)
Babel(self.app)
Babel(self.app, locale_selector=lambda: "en")

def test_no_license_pool(self):
with self.app.test_request_context("/"):
Expand Down Expand Up @@ -294,7 +294,7 @@ class TestLoadMethods(DatabaseTest):
def setup_method(self):
super(TestLoadMethods, self).setup_method()
self.app = Flask(__name__)
Babel(self.app)
Babel(self.app, locale_selector=lambda: "en")

def test_load_facets_from_request(self):
# The library has two EntryPoints enabled.
Expand Down Expand Up @@ -437,7 +437,7 @@ class MockManager(object):

self.app = Flask(__name__)
self.app.manager = MockManager()
Babel(self.app)
Babel(self.app, locale_selector=lambda: "en")

def activate_debug_mode(self):
"""Set a site-wide setting that controls whether
Expand Down
4 changes: 2 additions & 2 deletions core/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import string
from collections import Counter

import flask_sqlalchemy_session
import api.flask_sqlalchemy_session
import sqlalchemy
from money import Money
from sqlalchemy import distinct, select
Expand Down Expand Up @@ -542,7 +542,7 @@ def is_session(value):
:return: Boolean value indicating whether the value is a valid SQLAlchemy session or not
:rtype: bool
"""
return isinstance(value, (sqlalchemy.orm.session.Session, flask_sqlalchemy_session.flask_scoped_session))
return isinstance(value, (sqlalchemy.orm.session.Session, api.flask_sqlalchemy_session.flask_scoped_session))


def first_or_default(collection, default=None):
Expand Down
4 changes: 4 additions & 0 deletions docker/Dockerfile-es654.arm64
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk-java-home"

EXPOSE 9200 9300

RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo
RUN sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo
RUN sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo

RUN yum update -y \
&& yum install -y \
java-11-openjdk \
Expand Down
25 changes: 12 additions & 13 deletions requirements-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
#
apispec==5.1.1
apispec-webframeworks==0.5.2
Babel==2.8.0
Babel==2.15.0
boto3==1.23.2
botocore==1.26.2
certifi==2021.10.8
cffi==1.15.0
chardet==5.2.0
click==8.1.3
contextlib2==21.6.0
cryptography==37.0.2
cryptography==42.0.8
# defusedxml is required for SAML authentication
defusedxml==0.7.1
# docker is used for the LCP feature.
Expand All @@ -27,10 +27,9 @@ elasticsearch-dsl==6.4.0
enum34==1.1.10
expiringdict==1.2.1
feedparser==6.0.2
Flask==2.1.2
Flask-Babel==2.0.0
Flask==2.2.5
Flask-Babel==4.0.0
Flask-Cors==3.0.10
flask-sqlalchemy-session==1.1
flask-swagger-ui==4.11.1
# freezegun is used for unit tests.
freezegun==1.2.1
Expand All @@ -44,10 +43,10 @@ ipaddress==1.0.23
isbnlib==3.10.10
isodate==0.6.1
itsdangerous==2.1.2
Jinja2==3.1.2
Jinja2==3.1.4
jsonschema==3.2.0
loggly-python-handler==1.0.1
lxml==4.6.5
lxml==4.9.1
markupsafe==2.1.1
money==1.3.0
multipledispatch==0.6.0
Expand All @@ -61,7 +60,7 @@ newrelic==8.5.0
nltk==3.7
oauth2client==4.1.3
packaging==24.1
Pillow==9.1.1
Pillow==9.3.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
py-bcrypt==0.4
Expand All @@ -75,16 +74,16 @@ PyLD==1.0.5
pymarc==4.2.2
pyparsing==2.4.7
pypostalcode==0.3.6
pyOpenSSL==19.1.0
pyOpenSSL==24.1.0
pyspellchecker==0.5.5
pytz==2021.3
pytz==2022.7
python-dateutil==2.8.2
# TODO: python-Levenshtein is supposedly required for author name
# matching, but is not a core requirement; perhaps it can be removed.
python-Levenshtein==0.12.2
pyrsistent==0.16.1
# python-saml is required for SAML authentication
python3-saml==1.14.0
python3-saml==1.16.0
requests==2.31.0
requests-futures==1.0.0
# requests-mock is used for unit tests.
Expand All @@ -105,13 +104,13 @@ textblob==0.15.3
typing==3.7.4.3
unicodecsv==0.14.1
uritemplate==3.0.1
urllib3==1.26.9
urllib3==1.26.19
uszipcode==1.0.1
# watchtower is for Cloudwatch logging integration
watchtower==0.8.0
wcag-contrast-ratio==0.9
websocket-client==0.57.0
Werkzeug==2.0.3
Werkzeug==2.3.8
# xmlsec is required for SAML authentication
xmlsec==1.3.12

Expand Down
6 changes: 3 additions & 3 deletions tests/admin/controller/test_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ def test_redirect_to_sign_in(self):
assert 302 == response.status_code
location = response.headers.get("Location")
assert "sign_in" in location
assert "admin%2Fweb" in location
assert "collection%2Fa%252F%2528b%2529" in location
assert "book%2Fc%252F%2528d%2529" in location
assert "admin/web" in location # ???
assert "collection/a%252F%2528b%2529" in location
assert "book/c%252F%2528d%2529" in location

def test_redirect_to_library(self):
# If the admin doesn't have access to any libraries, they get a message
Expand Down
Loading

0 comments on commit ee17b76

Please sign in to comment.