Skip to content

Commit

Permalink
User hook function now have an OIDClient arg, use it to retrieve sett…
Browse files Browse the repository at this point in the history
…ings.
  • Loading branch information
regilero committed Jan 14, 2025
1 parent 4bb0530 commit 6a465f4
Show file tree
Hide file tree
Showing 8 changed files with 26 additions and 24 deletions.
3 changes: 3 additions & 0 deletions django_pyoidc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,6 @@ def __init__(self, op_name: str, session_id=None):

def get_settings(self) -> OIDCSettings:
return self.opsettings

def get_setting(self, name, default=None):
return self.opsettings.get(name, default)
3 changes: 2 additions & 1 deletion django_pyoidc/drf/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def authenticate(self, request):
tokens={
"access_token_jwt": access_token_jwt,
"access_token_claims": access_token_claims,
}
},
client=self.client,
)

if not user:
Expand Down
6 changes: 3 additions & 3 deletions django_pyoidc/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def call_function(self, setting_func_name, *args, **kwargs):
func = import_object(function_path, "")
return func(*args, **kwargs)

def call_get_user_function(self, tokens={}):
if self.opsettings.get("HOOK_GET_USER"):
def call_get_user_function(self, client: OIDCClient, tokens={}):
if self.opsettings.get("hook_get_user"):
logger.debug("OIDC, Calling user hook on get_user")
return self.call_function("HOOK_GET_USER", tokens)
return self.call_function("hook_get_user", tokens, client)
else:
logger.debug("OIDC, Calling get_user_by_email")
return get_user_by_email(tokens)
Expand Down
3 changes: 2 additions & 1 deletion django_pyoidc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,8 @@ def get(self, request, *args, **kwargs):
"access_token_jwt": access_token_jwt,
"access_token_claims": access_token_claims,
"id_token_claims": id_token_claims,
}
},
client=self.client,
)

if not user or not user.is_authenticated:
Expand Down
12 changes: 6 additions & 6 deletions docs/how-to.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Customize how token data is mapped to User attributes

By default, this library only uses the **email** field in a userinfo token to retrieve/create users.

However you can implement more complex behaviour by specifying a :ref:`HOOK_GET_USER` in your provider
However you can implement more complex behaviour by specifying a :ref:`hook_get_user` in your provider
configuration. In this guide we will look at the ``groups`` attribute in a userinfo token and set the
:attr:`is_staff <django.contrib.auth.models.User.is_staff>` attribute depending on the value.

Expand Down Expand Up @@ -115,7 +115,7 @@ We can see that here we want to lookup the ``groups`` key and test if ``admins``
return user
To have this function called instead of the default one, you need to modify your settings so that :ref:`HOOK_GET_USER` points to the function that we just wrote.
To have this function called instead of the default one, you need to modify your settings so that :ref:`hook_get_user` points to the function that we just wrote.

The value of this setting should be : ``<my_app>.oidc:login_function`` (see :ref:`Hook settings` for more information on this syntax).

Expand All @@ -129,7 +129,7 @@ Using a provider, edith your configuration like this :
FIXME **my_oidc_provider.get_config(login_uris_redirect_allowed_hosts=["myhost"]),
}
DJANGO_PYOIDC[my_oidc_provider.op_name]["HOOK_GET_USER"] = "<my_app>.oidc:get_user" # <- my_app is a placeholder, alter it for your root module
DJANGO_PYOIDC[my_oidc_provider.op_name]["hook_get_user"] = "<my_app>.oidc:get_user" # <- my_app is a placeholder, alter it for your root module
Expand All @@ -138,7 +138,7 @@ Add application-wide access control rules based on audiences

Open ID Connect supports a system of audience which can be used to indicate the list of applications a user has access to.

In order to implement access control based on the audience, you need to hook the :ref:`HOOK_GET_USER` to add your own logic.
In order to implement access control based on the audience, you need to hook the :ref:`hook_get_user` to add your own logic.

In this guide, we will start from what we did in :ref:`Customize how token data is mapped to User attributes` and add audience based access control.

Expand Down Expand Up @@ -179,7 +179,7 @@ Django provides a rich authentication system that handles groups and permissions
In this guide we will map Keycloak groups to Django groups. This allows one to manage group level permissions using Django system,
while keeping all the advantages of an Identity Provider to manage a user base.

In order to add users to groups on login, you need to hook the :ref:`HOOK_GET_USER`.
In order to add users to groups on login, you need to hook the :ref:`hook_get_user`.

We will start from what we did in :ref:`Customize how token data is mapped to User attributes` and add group management.

Expand Down Expand Up @@ -296,5 +296,5 @@ Here is an example of such a configuration :
You can then use those view names to redirect a user to one or the other provider.
TODO: what are the 'different' view names here?

Since settings are local to a provider, you can also provide different :ref:`HOOK_GET_USER` for each to implement custom
Since settings are local to a provider, you can also provide different :ref:`hook_get_user` for each to implement custom
behaviours based on which identity provider a user is coming from.
2 changes: 1 addition & 1 deletion docs/user.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ This function takes two arguments :

Since the user wasn't logged in, it is not yet attached to the request instance at this stage. As such trying to access ``request.user`` will return an unauthenticated user.

HOOK_GET_USER
hook_get_user
*************

Calls the provided function on user login. It takes two arguments :
Expand Down
15 changes: 6 additions & 9 deletions tests/e2e/test_app/callback.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.core.exceptions import PermissionDenied
Expand Down Expand Up @@ -49,7 +48,7 @@ def _debug_tokens(tokens={}):
print(tokens["id_token_claims"])


def get_user_with_resource_access_check(tokens={}):
def get_user_with_resource_access_check(client: OIDCClient, tokens={}):

_debug_tokens(tokens)
access_token_claims = (
Expand All @@ -67,9 +66,7 @@ def get_user_with_resource_access_check(tokens={}):
)

# Perform resource access checks
# FIXME: not sure we are always used with sso1, needa way to retrieve current op_name
# maybe from the request session, but then the request should be given as arg of user hook.
client_id = settings.DJANGO_PYOIDC["sso1"]["client_id"]
client_id = client.get_setting("client_id")
# warning for user with no access Keycloak would not generate the resource_access claim
# so we need to check absence of the claim also
if (resource_access and client_id not in resource_access) or (
Expand Down Expand Up @@ -106,7 +103,7 @@ def get_user_with_resource_access_check(tokens={}):
return user


def get_user_with_minimal_audiences_check(tokens={}):
def get_user_with_minimal_audiences_check(client: OIDCClient, tokens={}):

_debug_tokens(tokens)
access_token_claims = (
Expand All @@ -119,7 +116,7 @@ def get_user_with_minimal_audiences_check(tokens={}):

# Perform a minimal audience check
# Note: here not checking if client_id is in 'aud' because that's broken in Keycloak
client_id = settings.DJANGO_PYOIDC["sso1"]["client_id"]
client_id = client.get_setting("client_id")
if "azp" not in access_token_claims:
logger.error("Missing azp claim access_token")
raise PermissionDenied("You do not have access to this application.")
Expand Down Expand Up @@ -150,7 +147,7 @@ def get_user_with_minimal_audiences_check(tokens={}):
return user


def get_user_with_audiences_check(tokens={}):
def get_user_with_audiences_check(client: OIDCClient, tokens={}):

_debug_tokens(tokens)
access_token_claims = (
Expand Down Expand Up @@ -181,7 +178,7 @@ def get_user_with_audiences_check(tokens={}):
raise RuntimeError("Unknown type for audience claim")

# Perform audience check
if audiences and settings.DJANGO_PYOIDC["sso1"]["client_id"] not in audiences:
if audiences and client.get_setting("client_id") not in audiences:
logger.error("Failed audience check in access_token")
raise PermissionDenied("You do not have access to this application.")

Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def registerClient(
"HOOK_USER_LOGOUT": "tests.e2e.test_app.callback:logout_callback",
"login_uris_redirect_allowed_hosts": ["testserver"],
"login_redirection_requires_https": False,
"HOOK_GET_USER": "tests.e2e.test_app.callback:get_user_with_resource_access_check",
"hook_get_user": "tests.e2e.test_app.callback:get_user_with_resource_access_check",
},
# broken client_id
"sso3": {
Expand Down Expand Up @@ -407,7 +407,7 @@ def registerClient(
"login_redirection_requires_https": False,
"HOOK_USER_LOGIN": "tests.e2e.test_app.callback:login_callback",
"HOOK_USER_LOGOUT": "tests.e2e.test_app.callback:logout_callback",
"HOOK_GET_USER": "tests.e2e.test_app.callback:get_user_with_resource_access_check",
"hook_get_user": "tests.e2e.test_app.callback:get_user_with_resource_access_check",
},
# hook_get_user
"sso5": {
Expand All @@ -425,7 +425,7 @@ def registerClient(
"login_redirection_requires_https": False,
"HOOK_USER_LOGIN": "tests.e2e.test_app.callback:login_callback",
"HOOK_USER_LOGOUT": "tests.e2e.test_app.callback:logout_callback",
"HOOK_GET_USER": "tests.e2e.test_app.callback:get_user_with_minimal_audiences_check",
"hook_get_user": "tests.e2e.test_app.callback:get_user_with_minimal_audiences_check",
},
# API
"drf": {
Expand Down

0 comments on commit 6a465f4

Please sign in to comment.