Skip to content

Remove stuff that was deprecated for 2.5.0 #1425

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

Merged
merged 3 commits into from
Jun 12, 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
14 changes: 7 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ repos:
hooks:
- id: sphinx-lint
# Configuration for codespell is in pyproject.toml
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
exclude: (package-lock.json|/locale/)
additional_dependencies:
- tomli
# - repo: https://github.com/codespell-project/codespell
# rev: v2.3.0
# hooks:
# - id: codespell
# exclude: (package-lock.json|/locale/)
# additional_dependencies:
# - tomli
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
### Deprecated
### Removed
* #1425 Remove deprecated `RedirectURIValidator`, `WildcardSet` per #1345; `validate_logout_request` per #1274

### Fixed
### Security

Expand Down
34 changes: 0 additions & 34 deletions oauth2_provider/validators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
import warnings
from urllib.parse import urlsplit

from django.core.exceptions import ValidationError
Expand All @@ -19,20 +18,6 @@ class URIValidator(URLValidator):
regex = re.compile(scheme_re + host_re + port_re + path_re, re.IGNORECASE)


class RedirectURIValidator(URIValidator):
def __init__(self, allowed_schemes, allow_fragments=False):
warnings.warn("This class is deprecated and will be removed in version 2.5.0.", DeprecationWarning)
super().__init__(schemes=allowed_schemes)
self.allow_fragments = allow_fragments

def __call__(self, value):
super().__call__(value)
value = force_str(value)
scheme, netloc, path, query, fragment = urlsplit(value)
if fragment and not self.allow_fragments:
raise ValidationError("Redirect URIs must not contain fragments")


class AllowedURIValidator(URIValidator):
# TODO: find a way to get these associated with their form fields in place of passing name
# TODO: submit PR to get `cause` included in the parent class ValidationError params`
Expand Down Expand Up @@ -90,22 +75,3 @@ def __call__(self, value):
"%(name)s URI validation error. %(cause)s: %(value)s",
params={"name": self.name, "value": value, "cause": e},
)


##
# WildcardSet is a special set that contains everything.
# This is required in order to move validation of the scheme from
# URLValidator (the base class of URIValidator), to OAuth2Application.clean().


class WildcardSet(set):
"""
A set that always returns True on `in`.
"""

def __init__(self, *args, **kwargs):
warnings.warn("This class is deprecated and will be removed in version 2.5.0.", DeprecationWarning)
super().__init__(*args, **kwargs)

def __contains__(self, item):
return True
71 changes: 0 additions & 71 deletions oauth2_provider/views/oidc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import warnings
from urllib.parse import urlparse

from django.contrib.auth import logout
Expand Down Expand Up @@ -212,76 +211,6 @@ def _validate_claims(request, claims):
return True


def validate_logout_request(request, id_token_hint, client_id, post_logout_redirect_uri):
"""
Validate an OIDC RP-Initiated Logout Request.
`(prompt_logout, (post_logout_redirect_uri, application), token_user)` is returned.

`prompt_logout` indicates whether the logout has to be confirmed by the user. This happens if the
specifications force a confirmation, or it is enabled by `OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT`.
`post_logout_redirect_uri` is the validated URI where the User should be redirected to after the
logout. Can be None. None will redirect to "/" of this app. If it is set `application` will also
be set to the Application that is requesting the logout. `token_user` is the id_token user, which will
used to revoke the tokens if found.

The `id_token_hint` will be validated if given. If both `client_id` and `id_token_hint` are given they
will be validated against each other.
"""

warnings.warn("This method is deprecated and will be removed in version 2.5.0.", DeprecationWarning)

id_token = None
must_prompt_logout = True
token_user = None
if id_token_hint:
# Only basic validation has been done on the IDToken at this point.
id_token, claims = _load_id_token(id_token_hint)

if not id_token or not _validate_claims(request, claims):
raise InvalidIDTokenError()

token_user = id_token.user

if id_token.user == request.user:
# A logout without user interaction (i.e. no prompt) is only allowed
# if an ID Token is provided that matches the current user.
must_prompt_logout = False

# If both id_token_hint and client_id are given it must be verified that they match.
if client_id:
if id_token.application.client_id != client_id:
raise ClientIdMissmatch()

# The standard states that a prompt should always be shown.
# This behaviour can be configured with OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT.
prompt_logout = must_prompt_logout or oauth2_settings.OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT

application = None
# Determine the application that is requesting the logout.
if client_id:
application = get_application_model().objects.get(client_id=client_id)
elif id_token:
application = id_token.application

# Validate `post_logout_redirect_uri`
if post_logout_redirect_uri:
if not application:
raise InvalidOIDCClientError()
scheme = urlparse(post_logout_redirect_uri)[0]
if not scheme:
raise InvalidOIDCRedirectURIError("A Scheme is required for the redirect URI.")
if oauth2_settings.OIDC_RP_INITIATED_LOGOUT_STRICT_REDIRECT_URIS and (
scheme == "http" and application.client_type != "confidential"
):
raise InvalidOIDCRedirectURIError("http is only allowed with confidential clients.")
if scheme not in application.get_allowed_schemes():
raise InvalidOIDCRedirectURIError(f'Redirect to scheme "{scheme}" is not permitted.')
if not application.post_logout_redirect_uri_allowed(post_logout_redirect_uri):
raise InvalidOIDCRedirectURIError("This client does not have this redirect uri registered.")

return prompt_logout, (post_logout_redirect_uri, application), token_user


class RPInitiatedLogoutView(OIDCLogoutOnlyMixin, FormView):
template_name = "oauth2_provider/logout_confirm.html"
form_class = ConfirmLogoutForm
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ exclude = '''
'''

# Ref: https://github.com/codespell-project/codespell#using-a-config-file
[tool.codespell]
skip = '.git,package-lock.json,locale'
check-hidden = true
ignore-regex = '.*pragma: codespell-ignore.*'
# [tool.codespell]
# skip = '.git,package-lock.json,locale'
# check-hidden = true
# ignore-regex = '.*pragma: codespell-ignore.*'
# ignore-words-list = ''
105 changes: 1 addition & 104 deletions tests/test_oidc_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
from oauth2_provider.models import get_access_token_model, get_id_token_model, get_refresh_token_model
from oauth2_provider.oauth2_validators import OAuth2Validator
from oauth2_provider.settings import oauth2_settings
from oauth2_provider.views.oidc import (
RPInitiatedLogoutView,
_load_id_token,
_validate_claims,
validate_logout_request,
)
from oauth2_provider.views.oidc import RPInitiatedLogoutView, _load_id_token, _validate_claims

from . import presets

Expand Down Expand Up @@ -225,104 +220,6 @@ def mock_request_for(user):
return request


@pytest.mark.django_db
@pytest.mark.parametrize("ALWAYS_PROMPT", [True, False])
def test_deprecated_validate_logout_request(
oidc_tokens, public_application, other_user, rp_settings, ALWAYS_PROMPT
):
rp_settings.OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT = ALWAYS_PROMPT
oidc_tokens = oidc_tokens
application = oidc_tokens.application
client_id = application.client_id
id_token = oidc_tokens.id_token
assert validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=None,
post_logout_redirect_uri=None,
) == (True, (None, None), None)
assert validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=client_id,
post_logout_redirect_uri=None,
) == (True, (None, application), None)
assert validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=client_id,
post_logout_redirect_uri="http://example.org",
) == (True, ("http://example.org", application), None)
assert validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=id_token,
client_id=None,
post_logout_redirect_uri="http://example.org",
) == (ALWAYS_PROMPT, ("http://example.org", application), oidc_tokens.user)
assert validate_logout_request(
request=mock_request_for(other_user),
id_token_hint=id_token,
client_id=None,
post_logout_redirect_uri="http://example.org",
) == (True, ("http://example.org", application), oidc_tokens.user)
assert validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=id_token,
client_id=client_id,
post_logout_redirect_uri="http://example.org",
) == (ALWAYS_PROMPT, ("http://example.org", application), oidc_tokens.user)
with pytest.raises(InvalidIDTokenError):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint="111",
client_id=public_application.client_id,
post_logout_redirect_uri="http://other.org",
)
with pytest.raises(ClientIdMissmatch):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=id_token,
client_id=public_application.client_id,
post_logout_redirect_uri="http://other.org",
)
with pytest.raises(InvalidOIDCClientError):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=None,
post_logout_redirect_uri="http://example.org",
)
with pytest.raises(InvalidOIDCRedirectURIError):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=client_id,
post_logout_redirect_uri="example.org",
)
with pytest.raises(InvalidOIDCRedirectURIError):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=client_id,
post_logout_redirect_uri="imap://example.org",
)
with pytest.raises(InvalidOIDCRedirectURIError):
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=client_id,
post_logout_redirect_uri="http://other.org",
)
with pytest.raises(InvalidOIDCRedirectURIError):
rp_settings.OIDC_RP_INITIATED_LOGOUT_STRICT_REDIRECT_URIS = True
validate_logout_request(
request=mock_request_for(oidc_tokens.user),
id_token_hint=None,
client_id=public_application.client_id,
post_logout_redirect_uri="http://other.org",
)


@pytest.mark.django_db
def test_validate_logout_request(oidc_tokens, public_application, rp_settings):
oidc_tokens = oidc_tokens
Expand Down
96 changes: 1 addition & 95 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,7 @@
from django.core.validators import ValidationError
from django.test import TestCase

from oauth2_provider.validators import AllowedURIValidator, RedirectURIValidator, WildcardSet


@pytest.mark.usefixtures("oauth2_settings")
class TestValidators(TestCase):
def test_validate_good_uris(self):
validator = RedirectURIValidator(allowed_schemes=["https"])
good_uris = [
"https://example.com/",
"https://example.org/?key=val",
"https://example",
"https://localhost",
"https://1.1.1.1",
"https://127.0.0.1",
"https://255.255.255.255",
]
for uri in good_uris:
# Check ValidationError not thrown
validator(uri)

def test_validate_custom_uri_scheme(self):
validator = RedirectURIValidator(allowed_schemes=["my-scheme", "https", "git+ssh"])
good_uris = [
"my-scheme://example.com",
"my-scheme://example",
"my-scheme://localhost",
"https://example.com",
"HTTPS://example.com",
"git+ssh://example.com",
]
for uri in good_uris:
# Check ValidationError not thrown
validator(uri)

def test_validate_bad_uris(self):
validator = RedirectURIValidator(allowed_schemes=["https"])
self.oauth2_settings.ALLOWED_REDIRECT_URI_SCHEMES = ["https", "good"]
bad_uris = [
"http:/example.com",
"HTTP://localhost",
"HTTP://example.com",
"HTTP://example.com.",
"http://example.com/#fragment",
"123://example.com",
"http://fe80::1",
"git+ssh://example.com",
"my-scheme://example.com",
"uri-without-a-scheme",
"https://example.com/#fragment",
"good://example.com/#fragment",
" ",
"",
# Bad IPv6 URL, urlparse behaves differently for these
'https://["><script>alert()</script>',
]

for uri in bad_uris:
with self.assertRaises(ValidationError):
validator(uri)

def test_validate_wildcard_scheme__bad_uris(self):
validator = RedirectURIValidator(allowed_schemes=WildcardSet())
bad_uris = [
"http:/example.com#fragment",
"HTTP://localhost#fragment",
"http://example.com/#fragment",
"good://example.com/#fragment",
" ",
"",
# Bad IPv6 URL, urlparse behaves differently for these
'https://["><script>alert()</script>',
]

for uri in bad_uris:
with self.assertRaises(ValidationError, msg=uri):
validator(uri)

def test_validate_wildcard_scheme_good_uris(self):
validator = RedirectURIValidator(allowed_schemes=WildcardSet())
good_uris = [
"my-scheme://example.com",
"my-scheme://example",
"my-scheme://localhost",
"https://example.com",
"HTTPS://example.com",
"HTTPS://example.com.",
"git+ssh://example.com",
"ANY://localhost",
"scheme://example.com",
"at://example.com",
"all://example.com",
]
for uri in good_uris:
# Check ValidationError not thrown
validator(uri)
from oauth2_provider.validators import AllowedURIValidator


@pytest.mark.usefixtures("oauth2_settings")
Expand Down