# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import base64 from django.contrib.auth.hashers import PBKDF2PasswordHasher from django.contrib.auth.models import User from django.test import TestCase from member.models import Membership, Club from note.models import NoteUser from oauth2_provider.models import Application, AccessToken from ..models import Role, Permission class OAuth2FlowTestCase(TestCase): fixtures = ('initial', ) def setUp(self): self.user_password = "toto1234" hasher = PBKDF2PasswordHasher() self.user = User.objects.create( username="toto", password=hasher.encode(self.user_password, hasher.salt()), ) NoteUser.objects.create(user=self.user) membership = Membership.objects.create(user=self.user, club_id=1) membership.roles.add(Role.objects.get(name="Adhérent⋅e BDE")) membership.save() bde = Club.objects.get(name="BDE") view_user_perm = Permission.objects.get(pk=1) # View own user detail self.base_scope = f'{view_user_perm.pk}_{bde.pk}' def test_oauth2_authorization_code_flow(self): """ Ensure OAuth2 Authorization Code Flow work """ pass def test_oauth2_implicit_flow(self): """ Ensure OAuth2 Implicit Flow work """ pass def test_oauth2_resource_owner_password_credentials_flow(self): """ Ensure OAuth2 Resource Owner Password Credentials Flow work """ app = Application.objects.create( name="Test ROPB", client_type=Application.CLIENT_CONFIDENTIAL, authorization_grant_type=Application.GRANT_PASSWORD, user=self.user, hash_client_secret=False, algorithm=Application.NO_ALGORITHM, ) credential = base64.b64encode(f'{app.client_id}:{app.client_secret}'.encode('utf-8')).decode() # No token without real password resp = self.client.post('/o/token/', data={"grant_type": "password", "username": self.user, "password": "password"}, **{"Content-Type": 'application/x-www-form-urlencoded', "Http_Authorization": f'Basic {credential}'} ) self.assertEqual(resp.status_code, 400) resp = self.client.post('/o/token/', data={"grant_type": "password", "username": self.user, "password": self.user_password}, **{"Content-Type": 'application/x-www-form-urlencoded', "HTTP_Authorization": f'Basic {credential}'} ) self.assertEqual(resp.status_code, 200) access_token = AccessToken.objects.get(token=resp.json()['access_token']) self.assertEqual('refresh_token' in resp.json(), True) self.assertEqual(access_token.scope, '0_0') # token do nothing # RFC6749 4.3.2 allows use of scope in ROPB token access request resp = self.client.post('/o/token/', data={"grant_type": "password", #"client_id": app.client_id, "username": self.user, "password": self.user_password, "scope": self.base_scope}, **{"Content-Type": 'application/x-www-form-urlencoded', "HTTP_Authorization": f'Basic {credential}'} ) token = AccessToken.objects.get(token=resp.json()['access_token']) self.assertEqual(token.scope, self.base_scope) # token do nothing more than base_scope def test_oauth2_client_credentials(self): """ Ensure OAuth2 Client Credentials work """ app = Application.objects.create( name="Test client_credentials", client_type=Application.CLIENT_CONFIDENTIAL, authorization_grant_type=Application.GRANT_CLIENT_CREDENTIALS, user=self.user, hash_client_secret=False, algorithm=Application.NO_ALGORITHM, ) # No token without credential resp = self.client.post('/o/token/', data={"grant_type": "client_credentials"}, **{"Content-Type": 'application/x-www-form-urlencoded'} ) self.assertEqual(resp.status_code, 401) # Access with credential credential = base64.b64encode(f'{app.client_id}:{app.client_secret}'.encode('utf-8')).decode() resp = self.client.post('/o/token/', data={"grant_type": "client_credentials"}, **{'HTTP_Authorization': f'Basic {credential}', "Content-Type": 'application/x-www-form-urlencoded'} ) self.assertEqual(resp.status_code, 200) token = AccessToken.objects.get(token=resp.json()['access_token']) # Token do nothing, it should be have the useless scope self.assertEqual(token.scope, '0_0') # RFC6749 4.4.2 allows use of scope in client credential flow resp = self.client.post('/o/token/', data={"grant_type": "client_credentials", "scope": self.base_scope}, **{'http_Authorization': f'Basic {credential}', "Content-Type": 'application/x-www-form-urlencoded'} ) self.assertEqual(resp.status_code, 200) token = AccessToken.objects.get(token=resp.json()['access_token']) # Token can have access, it shouldn't have the useless scope self.assertEqual(token.scope, self.base_scope) def test_oidc_flow(self): """ Ensure OIDC Flow work """ pass