-
Notifications
You must be signed in to change notification settings - Fork 9
Fix anonymous access for other registries #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
51ca5ce
Parse authentication info from WWW-Authenticate header
tm-drtina-arista f4c5c81
Implement parsing via Read instance
tm-drtina-arista f81a4cb
Add tests for AuthInfo parsing
tm-drtina-arista 486e2b8
Collect parsing errors and combine them into a single error message
tm-drtina-arista File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| {-# LANGUAGE NamedFieldPuns #-} | ||
| {-# LANGUAGE OverloadedStrings #-} | ||
|
|
||
| ----------------------------------------------------------------------------- | ||
| -- | | ||
| -- Module : Hocker.Types.AuthInfo | ||
| -- Copyright : (C) 2026 Awake Networks | ||
| -- License : Apache-2.0 | ||
| -- Maintainer : Awake Networks <opensource@awakenetworks.com> | ||
| -- Stability : stable | ||
| ---------------------------------------------------------------------------- | ||
|
|
||
| module Hocker.Types.AuthInfo ( | ||
| AuthInfo(..), | ||
| parseWWWAuthHeader | ||
| ) where | ||
|
|
||
| import Control.Applicative ((<|>)) | ||
| import Data.Bifunctor (first) | ||
| import qualified Data.ByteString.Char8 as C8 | ||
| import qualified Data.CaseInsensitive as CI | ||
| import Data.Char (isAlphaNum, ord) | ||
| import Data.Either (partitionEithers) | ||
| import Data.List (intercalate) | ||
| import Data.List.NonEmpty (NonEmpty) | ||
| import qualified Data.List.NonEmpty as NonEmpty | ||
| import URI.ByteString (Absolute, URIRef, parseURI, strictURIParserOptions) | ||
| import Text.Read (readEither) | ||
| import Text.ParserCombinators.ReadP | ||
|
|
||
| import Hocker.Types.Exceptions | ||
|
|
||
|
|
||
| data AuthInfo = AuthInfo | ||
| { realm :: URIRef Absolute | ||
| , service :: C8.ByteString | ||
| , scope :: C8.ByteString | ||
| } | ||
| deriving (Show, Eq) | ||
|
|
||
| newtype AuthScheme = AuthScheme (CI.CI String) | ||
| deriving (Show, Eq) | ||
|
|
||
| data AuthParams = AuthParamsB64 String | AuthParamsArr [(CI.CI String, String)] | ||
| deriving (Show, Eq) | ||
|
|
||
| data Challenge = Challenge | ||
| { scheme :: AuthScheme | ||
| , params :: AuthParams | ||
| } deriving (Show, Eq) | ||
|
|
||
| isWhitespace :: Char -> Bool | ||
| isWhitespace ' ' = True | ||
| isWhitespace '\t' = True | ||
| isWhitespace _ = False | ||
|
|
||
| -- OWS = *( SP / HTAB ) | ||
| -- ; optional whitespace | ||
| -- BWS = OWS | ||
| -- ; "bad" whitespace | ||
| ows :: ReadP String | ||
| ows = munch isWhitespace | ||
|
|
||
| -- tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" | ||
| -- / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" | ||
| -- / DIGIT / ALPHA | ||
| -- ; any VCHAR, except delimiters | ||
| tchar :: ReadP Char | ||
| tchar = satisfy isTchar | ||
| where | ||
| isTchar c = isAlphaNum c || c `elem` ("!#$%&'*+-.^_`|~" :: String) | ||
|
|
||
| -- token = 1*tchar | ||
| token :: ReadP String | ||
| token = many1 tchar | ||
|
|
||
| -- token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" | ||
| token68 :: ReadP String | ||
| token68 = do | ||
| let validChar c = isAlphaNum c || c `elem` ("-._~+/" :: String) | ||
| part1 <- many1 $ satisfy validChar | ||
| part2 <- many $ satisfy (== '=') | ||
| return (part1 <> part2) | ||
|
|
||
| -- quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE | ||
| quotedString :: ReadP String | ||
| quotedString = do | ||
| _ <- char '"' | ||
| str <- many (qdtext <|> quotedPair) | ||
| _ <- char '"' | ||
| return str | ||
|
|
||
| -- obs-text = %x80-FF | ||
| -- qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text | ||
| qdtext :: ReadP Char | ||
| qdtext = satisfy isQdtext | ||
| where | ||
| isQdtext c = | ||
| let code = ord c | ||
| in isWhitespace c || code == 0x21 | ||
| || (code >= 0x23 && code <= 0x5B) | ||
| || (code >= 0x5D && code <= 0x7E) | ||
| || (code >= 0x80 && code <= 0xFF) | ||
|
|
||
| -- quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) | ||
| quotedPair :: ReadP Char | ||
| quotedPair = do | ||
| _ <- char '\\' | ||
| satisfy (\c -> let code = ord c in isWhitespace c || (code >= 0x21 && code <= 0x7E) || (code >= 0x80 && code <= 0xFF)) | ||
|
|
||
| authScheme :: ReadP AuthScheme | ||
| authScheme = (AuthScheme . CI.mk) <$> token | ||
|
|
||
| -- auth-param = token BWS "=" BWS ( token / quoted-string ) | ||
| authParam :: ReadP (CI.CI String, String) | ||
| authParam = do | ||
| key <- token | ||
| _ <- ows | ||
| _ <- char '=' | ||
| _ <- ows | ||
| val <- token <|> quotedString | ||
| return (CI.mk key, val) | ||
|
|
||
| -- Not part of RFC, extracted for readability | ||
| -- 1*SP ( token68 / [ ( "," / auth-param ) *( OWS "," [ OWS auth-param ] ) ] ) | ||
| -- | ||
| -- Note: We don't want to consume commas at the end to avoid accidentally consuming comma between two challenges. | ||
| -- The possible extra commas are being treated as "empty challenges" in `challenges` parser | ||
| authParams :: ReadP AuthParams | ||
| authParams = do | ||
| _ <- munch1 (== ' ') | ||
|
|
||
| let authToken = AuthParamsB64 <$> token68 | ||
| let authParams' = do | ||
| _ <- munch (\c -> isWhitespace c || c == ',') | ||
| res <- sepBy authParam (ows >> char ',' >> (munch (\c -> isWhitespace c || c == ','))) | ||
| pure $ AuthParamsArr res | ||
|
|
||
| authToken <|> authParams' | ||
|
|
||
| -- challenge = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param ) *( OWS "," [ OWS auth-param ] ) ] ) ] | ||
| challenge :: ReadP Challenge | ||
| challenge = do | ||
| s <- authScheme | ||
| ps <- authParams <|> (pure $ AuthParamsArr []) | ||
| return $ Challenge s ps | ||
|
|
||
| -- WWW-Authenticate = *( "," OWS ) challenge *( OWS "," [ OWS challenge ] ) | ||
| challenges :: ReadP (NonEmpty Challenge) | ||
| challenges = do | ||
| -- The header can start with an "empty" challenge | ||
| _ <- munch (\c -> isWhitespace c || c == ',') | ||
| let sepByAtLeastComma = (ows >> char ',' >> (munch (\c -> isWhitespace c || c == ','))) | ||
| cs <- sepBy1 challenge sepByAtLeastComma | ||
| -- It can also end with "empty" challenges | ||
| _ <- munch (\c -> isWhitespace c || c == ',') | ||
| return (NonEmpty.fromList cs) | ||
|
|
||
| newtype WWWAuthHeader = WWWAuthHeader (NonEmpty Challenge) | ||
|
|
||
| instance Read WWWAuthHeader where | ||
| readsPrec _ = readP_to_S $ WWWAuthHeader <$> (challenges <* eof) | ||
|
|
||
| challengeToAuthInfo :: Challenge -> Either String AuthInfo | ||
| challengeToAuthInfo c | ||
| | scheme c /= AuthScheme (CI.mk "bearer") = Left "Only Bearer challenges are supported" | ||
| | otherwise = case params c of | ||
| AuthParamsArr ps -> do | ||
| let lookupKey key = case lookup (CI.mk key) ps of | ||
| Just value -> Right $ C8.pack value | ||
| Nothing -> Left $ "Key '" <> key <> "' is missing" | ||
|
|
||
| rawRealm <- lookupKey "realm" | ||
| service <- lookupKey "service" | ||
| scope <- lookupKey "scope" | ||
|
|
||
| realm <- first (\e -> "Failed to parse realm as an absolute URL: " <> show e) $ parseURI strictURIParserOptions rawRealm | ||
| Right AuthInfo{ realm, service, scope } | ||
|
|
||
| AuthParamsB64 _ -> Left "Base64 auth params are not supported" | ||
|
|
||
| parseWWWAuthHeader :: C8.ByteString -> Either HockerException AuthInfo | ||
| parseWWWAuthHeader headerValue = do | ||
| WWWAuthHeader parsedChallenges <- first hockerException $ readEither $ C8.unpack headerValue | ||
|
|
||
| -- We take the first parsed AuthInfo we find. In case of multiple valid Bearer challenges we ignore the later ones. | ||
| let collectErrors xs = case partitionEithers $ NonEmpty.toList xs of | ||
| (_, authInfo:_) -> Right authInfo | ||
| (errors, _) -> Left $ hockerException ("Unable to extract AuthInfo from WWW-Authentication header:\n\t" <> intercalate "\n\t" errors) | ||
|
|
||
| collectErrors $ NonEmpty.map challengeToAuthInfo parsedChallenges | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.