Skip to content

Commit 9304bca

Browse files
authored
feat/(token): token endpoint is now configurable (#137)
2 parents a5e3533 + 3da16ee commit 9304bca

4 files changed

Lines changed: 104 additions & 23 deletions

File tree

openfga_sdk/credentials.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT.
1111
"""
1212

13-
from urllib.parse import urlparse
13+
from urllib.parse import urlparse, urlunparse
1414

1515
from openfga_sdk.exceptions import ApiValueError
1616

@@ -160,6 +160,33 @@ def configuration(self, value):
160160
"""
161161
self._configuration = value
162162

163+
def _parse_issuer(self, issuer: str):
164+
default_endpoint_path = "/oauth/token"
165+
166+
parsed_url = urlparse(issuer.strip())
167+
168+
try:
169+
parsed_url.port
170+
except ValueError as e:
171+
raise ApiValueError(e)
172+
173+
if parsed_url.netloc is None and parsed_url.path is None:
174+
raise ApiValueError("Invalid issuer")
175+
176+
if parsed_url.scheme == "":
177+
parsed_url = urlparse(f"https://{issuer}")
178+
elif parsed_url.scheme not in ("http", "https"):
179+
raise ApiValueError(
180+
f"Invalid issuer scheme {parsed_url.scheme} must be HTTP or HTTPS"
181+
)
182+
183+
if parsed_url.path in ("", "/"):
184+
parsed_url = parsed_url._replace(path=default_endpoint_path)
185+
186+
valid_url = urlunparse(parsed_url)
187+
188+
return valid_url
189+
163190
def validate_credentials_config(self):
164191
"""
165192
Check whether credentials configuration is valid
@@ -190,15 +217,5 @@ def validate_credentials_config(self):
190217
"configuration `{}` requires client_id, client_secret, api_audience and api_issuer defined for client_credentials method."
191218
)
192219
# validate token issuer
193-
combined_url = "https://" + self.configuration.api_issuer
194-
parsed_url = None
195-
try:
196-
parsed_url = urlparse(combined_url)
197-
except ValueError:
198-
raise ApiValueError(
199-
f"api_issuer `{self.configuration.api_issuer}` is invalid"
200-
)
201-
if parsed_url.netloc == "":
202-
raise ApiValueError(
203-
f"api_issuer `{self.configuration.api_issuer}` is invalid"
204-
)
220+
221+
self._parse_issuer(self.configuration.api_issuer)

test/client/client_test.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,12 +2314,16 @@ async def test_list_relations(self, mock_request):
23142314
Check whether a user is authorized to access an object
23152315
"""
23162316

2317+
def mock_check_requests(*args, **kwargs):
2318+
body = kwargs.get("body")
2319+
tuple_key = body.get("tuple_key")
2320+
if tuple_key["relation"] == "owner":
2321+
return mock_response('{"allowed": false, "resolution": "1234"}', 200)
2322+
return mock_response('{"allowed": true, "resolution": "1234"}', 200)
2323+
23172324
# First, mock the response
2318-
mock_request.side_effect = [
2319-
mock_response('{"allowed": true, "resolution": "1234"}', 200),
2320-
mock_response('{"allowed": false, "resolution": "1234"}', 200),
2321-
mock_response('{"allowed": true, "resolution": "1234"}', 200),
2322-
]
2325+
mock_request.side_effect = mock_check_requests
2326+
23232327
configuration = self.configuration
23242328
configuration.store_id = store_id
23252329
async with OpenFgaClient(configuration) as api_client:

test/credentials_test.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import openfga_sdk
1616
from openfga_sdk.credentials import CredentialConfiguration, Credentials
17+
from openfga_sdk.exceptions import ApiValueError
1718

1819

1920
class TestCredentials(IsolatedAsyncioTestCase):
@@ -172,3 +173,58 @@ def test_configuration_client_credentials_missing_api_audience(self):
172173
)
173174
with self.assertRaises(openfga_sdk.ApiValueError):
174175
credential.validate_credentials_config()
176+
177+
178+
class TestCredentialsIssuer(IsolatedAsyncioTestCase):
179+
def setUp(self):
180+
# Setup a basic configuration that can be modified per test case
181+
self.configuration = CredentialConfiguration(api_issuer="https://example.com")
182+
self.credentials = Credentials(
183+
method="client_credentials", configuration=self.configuration
184+
)
185+
186+
def test_valid_issuer_https(self):
187+
# Test a valid HTTPS URL
188+
self.configuration.api_issuer = "issuer.fga.example "
189+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
190+
self.assertEqual(result, "https://issuer.fga.example/oauth/token")
191+
192+
def test_valid_issuer_with_oauth_endpoint_https(self):
193+
# Test a valid HTTPS URL
194+
self.configuration.api_issuer = "https://example.com/oauth/token"
195+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
196+
self.assertEqual(result, "https://example.com/oauth/token")
197+
198+
def test_valid_issuer_with_some_endpoint_https(self):
199+
# Test a valid HTTPS URL
200+
self.configuration.api_issuer = "https://example.com/oauth/some/endpoint"
201+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
202+
self.assertEqual(result, "https://example.com/oauth/some/endpoint")
203+
204+
def test_valid_issuer_http(self):
205+
# Test a valid HTTP URL
206+
self.configuration.api_issuer = "fga.example/some_endpoint"
207+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
208+
self.assertEqual(result, "https://fga.example/some_endpoint")
209+
210+
def test_invalid_issuer_no_scheme(self):
211+
# Test an issuer URL without a scheme
212+
self.configuration.api_issuer = "https://issuer.fga.example:8080/some_endpoint "
213+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
214+
self.assertEqual(result, "https://issuer.fga.example:8080/some_endpoint")
215+
216+
def test_invalid_issuer_bad_scheme(self):
217+
# Test an issuer with an unsupported scheme
218+
self.configuration.api_issuer = "ftp://example.com"
219+
with self.assertRaises(ApiValueError):
220+
self.credentials._parse_issuer(self.configuration.api_issuer)
221+
222+
def test_invalid_issuer_with_port(self):
223+
# Test an issuer with an unsupported scheme
224+
self.configuration.api_issuer = "https://issuer.fga.example:8080 "
225+
result = self.credentials._parse_issuer(self.configuration.api_issuer)
226+
self.assertEqual(result, "https://issuer.fga.example:8080/oauth/token")
227+
228+
229+
if __name__ == "__main__":
230+
unittest.main()

test/sync/client/client_test.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,12 +2317,16 @@ def test_list_relations(self, mock_request):
23172317
Check whether a user is authorized to access an object
23182318
"""
23192319

2320+
def mock_check_requests(*args, **kwargs):
2321+
body = kwargs.get("body")
2322+
tuple_key = body.get("tuple_key")
2323+
if tuple_key["relation"] == "owner":
2324+
return mock_response('{"allowed": false, "resolution": "1234"}', 200)
2325+
return mock_response('{"allowed": true, "resolution": "1234"}', 200)
2326+
23202327
# First, mock the response
2321-
mock_request.side_effect = [
2322-
mock_response('{"allowed": true, "resolution": "1234"}', 200),
2323-
mock_response('{"allowed": false, "resolution": "1234"}', 200),
2324-
mock_response('{"allowed": true, "resolution": "1234"}', 200),
2325-
]
2328+
mock_request.side_effect = mock_check_requests
2329+
23262330
configuration = self.configuration
23272331
configuration.store_id = store_id
23282332
with OpenFgaClient(configuration) as api_client:

0 commit comments

Comments
 (0)