2525import json
2626import pytest
2727
28+ from typing import Optional , List , Dict
29+
30+ import jwt
2831import oauthlib .oauth2
2932import requests_oauthlib
3033
3437
3538
3639@pytest .fixture
37- def test_config ():
40+ def test_config () -> Optional [Dict ]:
41+ """Read the test configuration file the FLASK_CONFIG environment variable points to
42+
43+ A token can only be obtained if the configuration file pointed to by the FLASK_CONFIG environment variable
44+ contains required entries to set up OIDC for testing. An empty dict is returned if these are not present.
45+ The following are required:
46+
47+ {
48+ "web": { This entry is required to be the very first entry. If you don't like that,
49+ then externalize it into a separate file and point to it via OIDC_CLIENT_SECRETS
50+ "client_id": Server side client_id
51+ "client_secret": Server-side client_secret
52+ ...
53+ },
54+ "client": {
55+ "client_id": Test client client_id
56+ "client_secret": Test client secret
57+ "preferred_name": Asserted preferred_name of the client_id
58+ "OIDC_CLIENT_SECRETS": Point this to the same place as FLASK_CONFIG (to reduce the number of config files
59+ Returns:
60+ A dictionary of configuration or None if not configuration file is set
61+ """
3862 if 'FLASK_CONFIG' not in os .environ :
3963 LOGGER .info ('Missing test configuration via FLASK_CONFIG environment variable. Tests are limited' )
4064 return None
@@ -60,39 +84,57 @@ def client():
6084 yield client
6185
6286
63- @pytest .fixture
64- def oidc_token (test_config ):
87+ def oidc_token (config : Dict , scope : Optional [List ]):
6588 """Obtain an OIDC token to be used for client testing.
66-
67- A token can only be obtained if the configuration file pointed to by the FLASK_CONFIG environment variable
68- contains required entries to set up OIDC for testing. An empty dict is returned if these are not present.
69- The following are required:
70-
71- {
72- "web": { This entry is required to be the very first entry. If you don't like that,
73- then externalize it into a separate file and point to it via OIDC_CLIENT_SECRETS
74- "client_id": Server side client_id
75- "client_secret": Server-side client_secret
76- ...
77- },
78- "client": {
79- "client_id": Test client client_id
80- "client_secret": Test client secret
81- "preferred_name": Asserted preferred_name of the client_id
82- "OIDC_CLIENT_SECRETS": Point this to the same place as FLASK_CONFIG (to reduce the number of config files
83-
84- Yields:
89+ Args:
90+ config: Test configuration
91+ scope: Optional scope to request a token for. Defaults to ['openid']
92+ Returns:
8593 A dictionary containing the access token or None if configuration is lacking
8694 """
87- if test_config is None or 'client' not in test_config :
95+ if test_config is None or 'client' not in config :
8896 LOGGER .info ('Missing OIDC test client configuration. Tests will be limited' )
8997 return None
9098 for key in ['client_id' , 'client_secret' , 'preferred_name' ]:
91- if key not in test_config ['client' ]:
99+ if key not in config ['client' ]:
92100 LOGGER .info (f'Missing { key } in test client configuration. Tests will be limited' )
93101 return None
94- client = oauthlib .oauth2 .BackendApplicationClient (client_id = test_config ['client' ]['client_id' ])
102+ if scope is None :
103+ scope = ['openid' ]
104+ elif 'openid' not in scope :
105+ scope .insert (0 , 'openid' )
106+ client = oauthlib .oauth2 .BackendApplicationClient (client_id = config ['client' ]['client_id' ], scope = scope )
95107 oauth = requests_oauthlib .OAuth2Session (client = client )
96- return oauth .fetch_token (token_url = test_config ['web' ]['token_uri' ],
97- client_id = test_config ['client' ]['client_id' ],
98- client_secret = test_config ['client' ]['client_secret' ])
108+ return oauth .fetch_token (token_url = config ['web' ]['token_uri' ],
109+ client_id = config ['client' ]['client_id' ],
110+ client_secret = config ['client' ]['client_secret' ])
111+
112+
113+ @pytest .fixture
114+ def oidc_token_read (test_config ) -> Dict :
115+ """ Return an OIDC token with scope 'mrmat-python-api-flask-resource-read'
116+
117+ Args:
118+ test_config: The test configuration as per the test_config fixture
119+
120+ Returns:
121+ A Dict containing the desired token structure
122+ """
123+ token = oidc_token (test_config , ['mrmat-python-api-flask-resource-read' ])
124+ token ['jwt' ] = jwt .decode (token ['access_token' ], options = {"verify_signature" : False })
125+ return token
126+
127+
128+ @pytest .fixture
129+ def oidc_token_write (test_config ) -> Dict :
130+ """ Return an OIDC token with scope 'mrmat-python-api-flask-resource-write'
131+
132+ Args:
133+ test_config: The test configuration as per the test_config fixture
134+
135+ Returns:
136+ A Dict containing the desired token structure
137+ """
138+ token = oidc_token (test_config , ['mrmat-python-api-flask-resource-write' ])
139+ token ['jwt' ] = jwt .decode (token ['access_token' ], options = {"verify_signature" : False })
140+ return token
0 commit comments