Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: before_first_request deprecation #440

Merged
merged 5 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 7 additions & 29 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,15 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [3.8, 3.9]
requirements-level: [min, pypi]
python-version: ["3.9", "3.10", "3.11", "3.12"]
requirements-level: [pypi]
cache-service: [redis]
db-service: [postgresql14, postgresql13, mysql8]
exclude:
- python-version: 3.8
requirements-level: min

- python-version: 3.9
requirements-level: min
db-service: [postgresql14, mysql8]

include:
- db-service: postgresql14
DB_EXTRAS: "postgresql"

- db-service: postgresql13
DB_EXTRAS: "postgresql"

- db-service: mysql8
DB_EXTRAS: "mysql"

Expand All @@ -53,28 +44,15 @@ jobs:
EXTRAS: tests,admin,${{ matrix.DB_EXTRAS }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: setup.cfg

- name: Generate dependencies
run: |
python -m pip install --upgrade pip setuptools py wheel requirements-builder
requirements-builder -e "$EXTRAS" --level=${{ matrix.requirements-level }} setup.py > .${{ matrix.requirements-level }}-${{ matrix.python-version }}-requirements.txt

- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('.${{ matrix.requirements-level }}-${{ matrix.python-version }}-requirements.txt') }}

# TODO: Circular dependency. Invenio-Admin brings on Invenio-Accounts
# v1.x which brings Flask-Security which conflicts with
# Flask-Security-Invenio.
# pip install -r .${{ matrix.requirements-level }}-${{ matrix.python-version }}-requirements.txt
- name: Install dependencies
run: |
pip install ".[$EXTRAS]"
Expand Down
16 changes: 8 additions & 8 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@
#
# This file is part of Invenio.
# Copyright (C) 2015-2018 CERN.
# Copyright (C) 2022 Graz University of Technology.
# Copyright (C) 2022-2023 Graz University of Technology.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Sphinx configuration."""

import os
import sys

import sphinx.environment

from invenio_accounts import __version__

# -- General configuration ------------------------------------------------
Expand All @@ -38,6 +33,11 @@

celery_task_prefix = "()"

nitpick_ignore = [
("py:attr", "Meta"),
]


# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

Expand Down Expand Up @@ -324,8 +324,8 @@

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"https://docs.python.org/": None,
"http://pythonhosted.org/simplekv": None,
"python": ("https://docs.python.org/", None),
"simplekv": ("http://pythonhosted.org/simplekv", None),
}

# Autodoc configuraton.
Expand Down
69 changes: 64 additions & 5 deletions invenio_accounts/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# This file is part of Invenio.
# Copyright (C) 2015-2024 CERN.
# Copyright (C) 2021 TU Wien.
# Copyright (C) 2023-2024 Graz University of Technology.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.
Expand All @@ -15,9 +16,12 @@
from flask import Blueprint, abort, current_app, request_finished, session
from flask_kvsession import KVSessionExtension
from flask_login import LoginManager, user_logged_in, user_logged_out
from flask_menu import current_menu
from flask_principal import AnonymousIdentity
from flask_security import Security, user_confirmed
from invenio_db import db
from invenio_i18n import lazy_gettext as _
from invenio_theme.proxies import current_theme_icons
from passlib.registry import register_crypt_handler
from werkzeug.utils import cached_property

Expand Down Expand Up @@ -250,11 +254,6 @@ def _enable_session_activity(self, app):
user_logged_in.connect(csrf_token_reset, app)
user_logged_out.connect(logout_listener, app)
user_logged_out.connect(csrf_token_reset, app)
from .views.security import revoke_session, security
from .views.settings import blueprint

blueprint.route("/security/", methods=["GET"])(security)
blueprint.route("/sessions/revoke/", methods=["POST"])(revoke_session)


class InvenioAccountsREST(InvenioAccounts):
Expand Down Expand Up @@ -333,3 +332,63 @@ def make_session_permanent(self, app):
@app.before_request
def make_session_permanent():
session.permanent = True


def finalize_app(app):
"""Finalize app."""
set_default_config(app)
check_security_settings(app)
init_menu(app)


def set_default_config(app):
"""Set default values."""
app.config.setdefault(
"ACCOUNTS_SITENAME", app.config.get("THEME_SITENAME", "Invenio")
)
app.config.setdefault(
"ACCOUNTS_BASE_TEMPLATE",
app.config.get("BASE_TEMPLATE", "invenio_accounts/base.html"),
)
app.config.setdefault(
"ACCOUNTS_COVER_TEMPLATE",
app.config.get("COVER_TEMPLATE", "invenio_accounts/base_cover.html"),
)
app.config.setdefault(
"ACCOUNTS_SETTINGS_TEMPLATE",
app.config.get("SETTINGS_TEMPLATE", "invenio_accounts/settings/base.html"),
)


def check_security_settings(app):
"""Warn if session cookie is not secure in production."""
in_production = not (app.debug or app.testing)
secure = app.config.get("SESSION_COOKIE_SECURE")
if in_production and not secure:
app.logger.warning(
"SESSION_COOKIE_SECURE setting must be set to True to prevent the "
"session cookie from being leaked over an insecure channel."
)


def init_menu(app):
"""Init menu."""
current_menu.submenu("settings.security").register(
endpoint="invenio_accounts.security",
text=_(
"%(icon)s Security", icon=f'<i class="{current_theme_icons.shield}"></i>'
),
order=2,
)

# - Register menu
# - Change password
if app.config.get("SECURITY_CHANGEABLE", True):
current_menu.submenu("settings.change_password").register(
endpoint=f"{app.config['SECURITY_BLUEPRINT_NAME']}.change_password",
text=_(
"%(icon)s Change password",
icon=f'<i class="{current_theme_icons.key}"></i>',
),
order=1,
)
23 changes: 2 additions & 21 deletions invenio_accounts/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,7 @@

"""Invenio-accounts views."""

from flask import abort, current_app, request
from flask_security.views import anonymous_user_required
from flask_security.views import login as base_login

from .settings import blueprint
from .settings import login


@anonymous_user_required
@blueprint.route("/login")
def login(*args, **kwargs):
"""Disable login credential submission if local login is disabled."""
local_login_enabled = current_app.config.get("ACCOUNTS_LOCAL_LOGIN_ENABLED", True)

login_form_submitted = request.method == "POST"
if login_form_submitted and not local_login_enabled:
# only allow GET requests,
# avoid credential submission/login via POST
abort(404)

return base_login(*args, **kwargs)


__all__ = ("blueprint", "login")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: I would also keep the blueprint for backwards compat.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't know how, because the variable doesn't exist anymore in settings.py. that was the reason to move the def login function into settings.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and to be clear, as i understand it is also not possible to create this variable here, because i would have to call create_settings_blueprint here at a time where the current_app is not accessible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or should i do it that way, that i move the blueprint = Blueprint() outside of the create_settings_blueprint function?

__all__ = ("login",)
2 changes: 1 addition & 1 deletion invenio_accounts/views/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def role_to_dict(role):
)


def create_blueprint(app):
def create_rest_blueprint(app):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def create_rest_blueprint(app):
def create_blueprint(app):

nit: since we're in rest.py anyways and this is not exported anywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my thinking was, that i don't have to add lines like
from invenio_accounts.views.settings import create_blueprint as create_settings_blueprint in conftest.py, because i have to import both there. And in my opinion it looks clearer in the setup.cfg file for the entrypoints

"""Conditionally creates the blueprint."""
blueprint = Blueprint("invenio_accounts_rest_auth", __name__)

Expand Down
19 changes: 0 additions & 19 deletions invenio_accounts/views/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,16 @@
"""Invenio user management and authentication."""

from flask import abort, current_app, flash, redirect, render_template, request, url_for
from flask_breadcrumbs import register_breadcrumb
from flask_login import login_required
from flask_menu import register_menu
from flask_security import current_user
from invenio_db import db
from invenio_i18n import lazy_gettext as _
from invenio_theme.proxies import current_theme_icons
from speaklater import make_lazy_string

from ..forms import RevokeForm
from ..models import SessionActivity
from ..sessions import delete_session
from .settings import blueprint


@login_required
@register_menu(
blueprint,
"settings.security",
# NOTE: Menu item text (icon replaced by a user icon).
_(
"%(icon)s Security",
icon=make_lazy_string(
lambda: '<i class="{icon}"></i>'.format(icon=current_theme_icons.shield)
),
),
order=2,
)
@register_breadcrumb(blueprint, "breadcrumbs.settings.security", _("Security"))
def security():
"""View for security page."""
sessions = SessionActivity.query_by_user(user_id=current_user.get_id()).all()
Expand Down
Loading