Skip to content
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
16 changes: 0 additions & 16 deletions .flake8

This file was deleted.

2 changes: 1 addition & 1 deletion .github/utils/check_version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
import tomllib
from pathlib import Path

import tomllib

data = tomllib.loads(Path('pyproject.toml').read_text())
version = data['project']['version']
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,11 @@ jobs:
- name: Install ldap deps
run: sudo apt-get install libsasl2-dev libldap2-dev libssl-dev

- name: Update pip
run: |
pip install -U wheel
pip install -U setuptools
python -m pip install -U pip
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh

- name: Install tox
run: pip install tox
run: uv pip install --system tox

- name: Run tests
run: tox -e ${{ matrix.tox }}
4 changes: 0 additions & 4 deletions .isort.cfg

This file was deleted.

15 changes: 6 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
#
# Flask-Multipass documentation build configuration file, created by
# sphinx-quickstart on Mon Mar 30 11:56:17 2015.
Expand All @@ -12,12 +11,11 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import importlib.metadata
import os

import sys
from unittest.mock import MagicMock


# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand All @@ -37,7 +35,7 @@
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx'
'sphinx.ext.intersphinx',
]

# Add any paths that contain templates here, relative to this directory.
Expand All @@ -62,7 +60,6 @@
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
import importlib.metadata
try:
release = importlib.metadata.version('flask-multipass')
except importlib.metadata.PackageNotFoundError:
Expand Down Expand Up @@ -124,7 +121,7 @@
html_theme = 'flask'
html_theme_options = {
'index_logo_height': '120px',
'index_logo': 'flask-multipass-long.png'
'index_logo': 'flask-multipass-long.png',
}

# Add any paths that contain custom themes here, relative to this directory.
Expand Down Expand Up @@ -166,7 +163,7 @@
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html']
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'],
}

# Additional templates that should be rendered to pages, maps page names to
Expand Down Expand Up @@ -264,7 +261,7 @@
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'flask-multipass', 'Flask-Multipass Documentation',
[author], 1)
[author], 1),
]

# If true, show URL addresses after external links.
Expand Down
1 change: 0 additions & 1 deletion example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from flask_multipass import Multipass
from flask_multipass.providers.sqlalchemy import SQLAlchemyAuthProviderBase, SQLAlchemyIdentityProviderBase


application = app = Flask(__name__)
app.debug = True
app.secret_key = 'fma-example'
Expand Down
21 changes: 13 additions & 8 deletions flask_multipass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
# Flask-Multipass is free software; you can redistribute it
# and/or modify it under the terms of the Revised BSD License.

from .auth import AuthProvider
from .core import Multipass
from .data import AuthInfo, IdentityInfo
from .exceptions import (AuthenticationFailed, GroupRetrievalFailed, IdentityRetrievalFailed, InvalidCredentials,
MultipassException, NoSuchUser)
from .group import Group
from .identity import IdentityProvider

from flask_multipass.auth import AuthProvider
from flask_multipass.core import Multipass
from flask_multipass.data import AuthInfo, IdentityInfo
from flask_multipass.exceptions import (
AuthenticationFailed,
GroupRetrievalFailed,
IdentityRetrievalFailed,
InvalidCredentials,
MultipassException,
NoSuchUser,
)
from flask_multipass.group import Group
from flask_multipass.identity import IdentityProvider

__all__ = ('Multipass', 'AuthProvider', 'IdentityProvider', 'AuthInfo', 'IdentityInfo', 'Group', 'MultipassException',
'AuthenticationFailed', 'IdentityRetrievalFailed', 'GroupRetrievalFailed', 'NoSuchUser',
Expand Down
2 changes: 1 addition & 1 deletion flask_multipass/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AuthProvider(metaclass=SupportsMeta):
SupportsMeta.callable(lambda cls: cls.login_form is not None,
'login_form is set'): 'process_local_login',
SupportsMeta.callable(lambda cls: cls.login_form is None,
'login_form is not set'): 'initiate_external_login'
'login_form is not set'): 'initiate_external_login',
}
#: The entry point to lookup providers (do not override this!)
_entry_point = 'flask_multipass.auth_providers'
Expand Down
53 changes: 29 additions & 24 deletions flask_multipass/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
from flask_multipass.auth import AuthProvider
from flask_multipass.exceptions import GroupRetrievalFailed, IdentityRetrievalFailed, MultipassException
from flask_multipass.identity import IdentityProvider
from flask_multipass.util import (get_canonical_provider_map, get_provider_base, get_state, resolve_provider_type,
validate_provider_map)

from flask_multipass.util import (
get_canonical_provider_map,
get_provider_base,
get_state,
resolve_provider_type,
validate_provider_map,
)

# If SQLAlchemy is available, handle its AssociationCollection as a
# multi-value type for search criteria
Expand Down Expand Up @@ -77,7 +81,7 @@ def init_app(self, app):

@property
def auth_providers(self):
"""Returns a read-only dict of the active auth providers"""
"""Returns a read-only dict of the active auth providers."""
return get_state().auth_providers

@property
Expand All @@ -90,7 +94,7 @@ def single_auth_provider(self):

@property
def identity_providers(self):
"""Returns a read-only dict of the active identity providers"""
"""Returns a read-only dict of the active identity providers."""
return get_state().identity_providers

@property
Expand All @@ -116,7 +120,7 @@ def register_provider(self, cls, type_):
registry[type_] = cls

def redirect_success(self):
"""Redirects to whatever page should be displayed after login"""
"""Redirects to whatever page should be displayed after login."""
return redirect(self._get_next_url())

def set_next_url(self):
Expand Down Expand Up @@ -145,7 +149,7 @@ def validate_next_url(self, url):
return not url_info.netloc or url_info.netloc == request.host

def process_login(self, provider=None):
"""Handles the login process
"""Handles the login process.

This should be registered in the Flask routing system and
accept GET and POST requests on two URLs, one of them
Expand Down Expand Up @@ -202,13 +206,14 @@ def login_finished(self, identity_info):
:param identity_info: An :class:`.IdentityInfo` instance or
a list of them
"""
assert self.identity_callback is not None, \
'No identity callback has been registered. Register one using ' \
assert self.identity_callback is not None, (
'No identity callback has been registered. Register one using '
'Register one using the Multipass.identity_handler decorator.'
)
return self.identity_callback(identity_info)

def handle_auth_success(self, auth_info):
"""Called after a successful authentication
"""Called after a successful authentication.

This method calls :meth:`login_finished` with the found
identity. If ``MULTIPASS_ALL_MATCHING_IDENTITIES`` is set, it
Expand Down Expand Up @@ -238,7 +243,7 @@ def handle_auth_success(self, auth_info):
if not current_app.config['MULTIPASS_ALL_MATCHING_IDENTITIES']:
break
if not identities and current_app.config['MULTIPASS_REQUIRE_IDENTITY']:
raise IdentityRetrievalFailed("No identity found", provider=auth_info.provider)
raise IdentityRetrievalFailed('No identity found', provider=auth_info.provider)
session['_multipass_login_provider'] = auth_info.provider.name
if current_app.config['MULTIPASS_ALL_MATCHING_IDENTITIES']:
response = self.login_finished(identities)
Expand All @@ -247,7 +252,7 @@ def handle_auth_success(self, auth_info):
return response or self.redirect_success()

def handle_auth_error(self, exc, redirect_to_login=False):
"""Handles an authentication failure
"""Handles an authentication failure.

:param exc: The exception indicating the error.
:param redirect_to_login: Returns a redirect response to the
Expand All @@ -260,7 +265,7 @@ def handle_auth_error(self, exc, redirect_to_login=False):
return redirect(url_for(current_app.config['MULTIPASS_LOGIN_ENDPOINT']))

def render_template(self, template_key, **kwargs):
"""Renders a template configured in the app config
"""Renders a template configured in the app config.

:param template_key: The template key to insert in the config
option name ``MULTIPASS_*_TEMPLATE``
Expand Down Expand Up @@ -303,7 +308,7 @@ def login_check(self, callback):
return callback

def refresh_identity(self, identifier, multipass_data):
"""Retrieves user identity information for an existing identity
"""Retrieves user identity information for an existing identity.

:param identifier: The `identifier` from :class:`.IdentityInfo`
:param multipass_data: The `multipass_data` dict from
Expand Down Expand Up @@ -343,7 +348,7 @@ def get_identity(self, provider, identifier):
return provider.get_identity(identifier)

def search_identities(self, providers=None, exact=False, **criteria):
"""Searches user identities matching certain criteria
"""Searches user identities matching certain criteria.

:param providers: A list of providers to search in. If not
specified, all providers are searched.
Expand Down Expand Up @@ -414,7 +419,7 @@ def search_identities_ex(self, providers=None, exact=False, limit=None, criteria
return found_identities, total

def get_group(self, provider, name):
"""Returns a specific group
"""Returns a specific group.

:param provider: The name of the provider containing the group.
:param name: The name of the group.
Expand All @@ -427,7 +432,7 @@ def get_group(self, provider, name):
return provider.get_group(name)

def search_groups(self, name, providers=None, exact=False):
"""Searches groups by name
"""Searches groups by name.

:param name: The name to search for.
:param providers: A list of providers to search in. If not
Expand All @@ -444,7 +449,7 @@ def search_groups(self, name, providers=None, exact=False):
yield from provider.search_groups(name, exact=exact)

def is_identity_in_group(self, provider, identity_identifier, group_name):
"""Checks if a user identity is in a group
"""Checks if a user identity is in a group.

:param provider: The name of the provider containing the group.
:param identity_identifier: The identifier of the user.
Expand All @@ -454,7 +459,7 @@ def is_identity_in_group(self, provider, identity_identifier, group_name):
return identity_identifier in group

def _create_providers(self, key, base):
"""Instantiates all providers
"""Instantiates all providers.

:param key: The key to insert into the config option name
``MULTIPASS_*_PROVIDERS``
Expand All @@ -473,7 +478,7 @@ def _create_providers(self, key, base):
return providers

def _create_login_rule(self):
"""Creates the login URL rule if necessary"""
"""Creates the login URL rule if necessary."""
endpoint = current_app.config['MULTIPASS_LOGIN_ENDPOINT']
rules = current_app.config['MULTIPASS_LOGIN_URLS']
if rules is None:
Expand All @@ -494,7 +499,7 @@ def _get_next_url(self):
return url_for(current_app.config['MULTIPASS_SUCCESS_ENDPOINT'])

def _login_selector(self):
"""Shows the login method (auth provider) selector"""
"""Shows the login method (auth provider) selector."""
next_url = request.args.get('next')
auth_failed = session.pop('_multipass_auth_failed', False)
login_endpoint = current_app.config['MULTIPASS_LOGIN_ENDPOINT']
Expand All @@ -505,12 +510,12 @@ def _login_selector(self):
auth_failed=auth_failed, login_endpoint=login_endpoint)

def _login_external(self, provider):
"""Starts the external login process"""
"""Starts the external login process."""
self.set_next_url()
return provider.initiate_external_login()

def handle_login_form(self, provider, data):
"""Handles the submitted and validated login form
"""Handles the submitted and validated login form.

This returns a response in case of a successful login.
In case of an error, it is handled internally and the
Expand All @@ -528,7 +533,7 @@ def handle_login_form(self, provider, data):
return response

def _login_form(self, provider):
"""Starts the local form-based login process"""
"""Starts the local form-based login process."""
form = provider.login_form()
if not form.is_submitted():
self.set_next_url()
Expand Down
2 changes: 1 addition & 1 deletion flask_multipass/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self, provider, secure_login=None, **data):
self.secure_login = is_secure_login(self)

def map(self, mapping):
"""Creates a new instance with transformed data keys
"""Creates a new instance with transformed data keys.

:param mapping: The dict mapping the current data keys to the
the keys that are expected by the identity
Expand Down
8 changes: 4 additions & 4 deletions flask_multipass/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# and/or modify it under the terms of the Revised BSD License.

class MultipassException(Exception):
"""Base class for Multipass exceptions"""
"""Base class for Multipass exceptions."""

def __init__(self, message=None, details=None, provider=None):
args = (message,) if message else ()
Expand All @@ -18,7 +18,7 @@ class AuthenticationFailed(MultipassException):
"""
Indicates an authentication failure that was caused by the user,
e.g. by entering the wrong credentials or not authorizing the
application
application.
"""


Expand All @@ -37,8 +37,8 @@ def __init__(self, details=None, provider=None):


class IdentityRetrievalFailed(MultipassException):
"""Indicates a failure while retrieving identity information"""
"""Indicates a failure while retrieving identity information."""


class GroupRetrievalFailed(MultipassException):
"""Indicates a failure while retrieving group information"""
"""Indicates a failure while retrieving group information."""
2 changes: 1 addition & 1 deletion flask_multipass/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class Group(metaclass=SupportsMeta):
"""Base class for groups
"""Base class for groups.

:param provider: The identity provider managing the group.
:param name: The unique name of the group.
Expand Down
Loading