Skip to content

Commit

Permalink
core: improve anon/auth token logic (#148)
Browse files Browse the repository at this point in the history
* core: TokenAuth request_token fix missing auth

the method is intended to request authenticated
token, per pydocs, but was passing an headers
which was always missing Authorization.

* core: use token in auth in subsequent requests

if a token was saved in auth,
it shall be used in subsequent requests.

This avoid a situation where:
to upload a blob, first is done anonymously, then
retry with token
then upload a manifest, avoid the attempt to upload
anonymously if a token was present in the previous
flow

* core: if 401 on 2nd attempt, avoid anon tokens

in the first flow using auth backend for token:
1. try do_request with no auths at all
2. the attempt to gain an anon token is success,
but then the request fails with 401
3. at this point, in the third attempt, give
chance to the flow to request a token but avoid
any anon tokens.

Please note: this happens effectively only on the
first run of the flow. Subsequent do_request flow
invocations should just succeed now on the 1st
request by re-using the token --simplified
behaviour introduced with this proposal

* guard as headers is Optional
* implement review request

* Revert "implement review request"

This reverts commit 102381c.
This reverts commit 1e891d2.
This reverts commit 6e22667.

this was taken care in #153

This reverts commit 10e010b.

* implement review comment about anon/req token

from: #148 (comment)

> And if the basic auth is there, skip over asking for an anon token

as it stands, in case the basic auth are present,
these are exchanged for the request token.

Signed-off-by: tarilabs <[email protected]>

---------

Signed-off-by: tarilabs <[email protected]>
  • Loading branch information
tarilabs authored Sep 24, 2024
1 parent 625c6ff commit 81c4b04
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ env
__pycache__
.python-version
.venv
.vscode
build
dist
25 changes: 13 additions & 12 deletions oras/auth/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ def authenticate_request(

h = auth_utils.parse_auth_header(authHeaderRaw)

# First try to request an anonymous token
logger.debug("No Authorization, requesting anonymous token")
anon_token = self.request_anonymous_token(h)
if anon_token:
logger.debug("Successfully obtained anonymous token!")
self.token = anon_token
headers["Authorization"] = "Bearer %s" % self.token
return headers, True

# Next try for logged in token
# if no basic auth, try by request an anonymous token
if not hasattr(self, "_basic_auth"):
anon_token = self.request_anonymous_token(h)
if anon_token:
logger.debug("Successfully obtained anonymous token!")
self.token = anon_token
headers["Authorization"] = "Bearer %s" % self.token
return headers, True

# basic auth is available, try using auth token
token = self.request_token(h)
if token:
self.token = token
Expand All @@ -95,7 +95,7 @@ def authenticate_request(

def request_token(self, h: auth_utils.authHeader) -> bool:
"""
Request an authenticated token and save for later.s
Request an authenticated token and save for later.
"""
params = {}
headers = {}
Expand Down Expand Up @@ -124,6 +124,7 @@ def request_token(self, h: auth_utils.authHeader) -> bool:
# Set Basic Auth to receive token
headers["Authorization"] = "Basic %s" % self._basic_auth

logger.debug(f"Requesting auth token for: {h}")
authResponse = self.session.get(h.realm, headers=headers, params=params) # type: ignore

if authResponse.status_code != 200:
Expand All @@ -150,7 +151,7 @@ def request_anonymous_token(self, h: auth_utils.authHeader) -> bool:
if h.scope:
params["scope"] = h.scope

logger.debug(f"Final params are {params}")
logger.debug(f"Requesting anon token with params: {params}")
response = self.session.request("GET", h.realm, params=params)
if response.status_code != 200:
logger.debug(f"Response for anon token failed: {response.text}")
Expand Down
3 changes: 3 additions & 0 deletions oras/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ def __init__(self, lookup: dict):
if key in ["realm", "service", "scope"]:
setattr(self, key, lookup[key])

def __repr__(self):
return f"authHeader(lookup={{'service': {repr(self.service)}, 'realm': {repr(self.realm)}, 'scope': {repr(self.scope)}}})"


def parse_auth_header(authHeaderRaw: str) -> authHeader:
"""
Expand Down
4 changes: 3 additions & 1 deletion oras/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,9 @@ def do_request(
:param stream: stream the responses
:type stream: bool
"""
# Make the request and return to calling function, unless requires auth
# Make the request and return to calling function, but attempt to use auth token if previously obtained
if headers is not None and isinstance(self.auth, oras.auth.TokenAuth):
headers.update(self.auth.get_auth_header())
response = self.session.request(
method,
url,
Expand Down

0 comments on commit 81c4b04

Please sign in to comment.