Skip to content

Commit

Permalink
Fix oauth2 with custom ca bundle
Browse files Browse the repository at this point in the history
fixes #1096
  • Loading branch information
mdellweg committed Oct 10, 2024
1 parent 556bc07 commit 0c06cef
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGES/1096.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed OAuth2 not using the provided CA bundle.
49 changes: 28 additions & 21 deletions pulp-glue/pulp_glue/common/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,30 @@ def __init__(
client_secret: str,
token_url: str,
scopes: t.Optional[t.List[str]] = None,
verify: t.Optional[t.Union[str, bool]] = None,
):
self.token_auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
self.token_url = token_url
self.scopes = scopes
self._token_server_auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
self._token_url = token_url
self._scopes = scopes
self._verify = verify

self.access_token: t.Optional[str] = None
self.expire_at: t.Optional[datetime] = None
self._access_token: t.Optional[str] = None
self._expire_at: t.Optional[datetime] = None

def __call__(self, request: requests.PreparedRequest) -> requests.PreparedRequest:
if self.expire_at is None or self.expire_at < datetime.now():
self.retrieve_token()
if self._expire_at is None or self._expire_at < datetime.now():
self._retrieve_token()

assert self.access_token is not None
assert self._access_token is not None

request.headers["Authorization"] = f"Bearer {self.access_token}"
request.headers["Authorization"] = f"Bearer {self._access_token}"

# Call to untyped function "register_hook" in typed context
request.register_hook("response", self.handle401) # type: ignore[no-untyped-call]
request.register_hook("response", self._handle401) # type: ignore[no-untyped-call]

return request

def handle401(
def _handle401(
self,
response: requests.Response,
**kwargs: t.Any,
Expand All @@ -48,22 +50,22 @@ def handle401(
# If we get this far, probably the token is not valid anymore.

# Try to reach for a new token once.
self.retrieve_token()
self._retrieve_token()

assert self.access_token is not None
assert self._access_token is not None

# Consume content and release the original connection
# to allow our new request to reuse the same one.
response.content
response.close()
prepared_new_request = response.request.copy()

prepared_new_request.headers["Authorization"] = f"Bearer {self.access_token}"
prepared_new_request.headers["Authorization"] = f"Bearer {self._access_token}"

# Avoid to enter into an infinity loop.
# Call to untyped function "deregister_hook" in typed context
prepared_new_request.deregister_hook( # type: ignore[no-untyped-call]
"response", self.handle401
"response", self._handle401
)

# "Response" has no attribute "connection"
Expand All @@ -73,18 +75,23 @@ def handle401(

return new_response

def retrieve_token(self) -> None:
def _retrieve_token(self) -> None:
data = {
"grant_type": "client_credentials",
}

if self.scopes:
data["scope"] = " ".join(self.scopes)
if self._scopes:
data["scope"] = " ".join(self._scopes)

response: requests.Response = requests.post(self.token_url, data=data, auth=self.token_auth)
response: requests.Response = requests.post(
self._token_url,
data=data,
auth=self._token_server_auth,
verify=self._verify,
)

response.raise_for_status()

token = response.json()
self.expire_at = datetime.now() + timedelta(seconds=token["expires_in"])
self.access_token = token["access_token"]
self._expire_at = datetime.now() + timedelta(seconds=token["expires_in"])
self._access_token = token["access_token"]
13 changes: 12 additions & 1 deletion pulp-glue/pulp_glue/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class PulpContext:
fake_mode: In fake mode, no modifying calls will be performed.
Where possible, instead of failing, the requested result will be faked.
This implies `safe_calls_only=True` on the `api_kwargs`.
verify: A boolean or a path to the CA bundle.
"""

def echo(self, message: str, nl: bool = True, err: bool = False) -> None:
Expand Down Expand Up @@ -268,10 +269,18 @@ def __init__(
timeout: t.Union[int, datetime.timedelta] = 300,
domain: str = "default",
fake_mode: bool = False,
verify: t.Optional[t.Union[bool, str]] = None,
) -> None:
self._api: t.Optional[OpenAPI] = None
self._api_root: str = api_root
self._api_kwargs = api_kwargs
self.verify = verify
if self.verify is None:
# Regrets, regrets...
self.verify = self._api_kwargs.pop("validate_certs", True)
if self.verify is True:
# If this is "only" true and we have the PULP_CA_BUNDLE variable set, use it.
self.verify = os.environ.get("PULP_CA_BUNDLE", True)
self._needed_plugins: t.List[PluginRequirement] = [
PluginRequirement("core", specifier=">=3.11.0")
]
Expand Down Expand Up @@ -404,7 +413,9 @@ def api(self) -> OpenAPI:
)
try:
self._api = OpenAPI(
doc_path=f"{self._api_root}api/v3/docs/api.json", **self._api_kwargs
doc_path=f"{self._api_root}api/v3/docs/api.json",
verify=self.verify,
**self._api_kwargs,
)
except OpenAPIError as e:
raise PulpException(str(e))
Expand Down
9 changes: 3 additions & 6 deletions pulp-glue/pulp_glue/common/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class OpenAPI:
auth_provider: Object that returns requests auth objects according to the api spec.
cert: Client certificate used for auth.
key: Matching key for `cert` if not already included.
validate_certs: Whether to check server TLS certificates agains a CA.
verify: Whether to check server TLS certificates agains a CA (requests semantic).
refresh_cache: Whether to fetch the api doc regardless.
safe_calls_only: Flag to disallow issuing POST, PUT, PATCH or DELETE calls.
debug_callback: Callback that will be called with strings useful for logging or debugging.
Expand All @@ -167,14 +167,14 @@ def __init__(
auth_provider: t.Optional[AuthProviderBase] = None,
cert: t.Optional[str] = None,
key: t.Optional[str] = None,
validate_certs: bool = True,
verify: t.Optional[t.Union[bool, str]] = True,
refresh_cache: bool = False,
safe_calls_only: bool = False,
debug_callback: t.Optional[t.Callable[[int, str], t.Any]] = None,
user_agent: t.Optional[str] = None,
cid: t.Optional[str] = None,
):
if not validate_certs:
if verify is False:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

self.debug_callback: t.Callable[[int, str], t.Any] = debug_callback or (lambda i, x: None)
Expand Down Expand Up @@ -206,9 +206,6 @@ def __init__(
self._session.headers.update(headers)
self._session.max_redirects = 0

verify: t.Optional[t.Union[bool, str]] = validate_certs and os.environ.get(
"PULP_CA_BUNDLE", True
)
session_settings = self._session.merge_environment_settings(
base_url, {}, None, verify, None
)
Expand Down
4 changes: 2 additions & 2 deletions pulp-glue/tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def _requests_post_mocked(url: str, data: t.Dict[str, t.Any], **kwargs: t.Any):

monkeypatch.setattr("requests.post", _requests_post_mocked)

OAuth2ClientCredentialsAuth(token_url="", client_id="", client_secret="").retrieve_token()
OAuth2ClientCredentialsAuth(token_url="", client_id="", client_secret="")._retrieve_token()

OAuth2ClientCredentialsAuth(
token_url="", client_id="", client_secret="", scopes=[]
).retrieve_token()
)._retrieve_token()
2 changes: 2 additions & 0 deletions pulpcore/cli/common/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def oauth2_client_credentials_auth(
token_url=flow["tokenUrl"],
# Try to request all possible scopes.
scopes=flow["scopes"],
verify=self.pulp_ctx.verify,
)
else:
self._memoized[key] = OAuth2ClientCredentialsAuth(
Expand All @@ -276,6 +277,7 @@ def oauth2_client_credentials_auth(
token_url=flow["tokenUrl"],
# Try to request all possible scopes.
scopes=flow["scopes"],
verify=self.pulp_ctx.verify,
)
return self._memoized[key]

Expand Down

0 comments on commit 0c06cef

Please sign in to comment.