-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #272 from xmidt-org/feature/remove-credentials
Feature/remove credentials
- Loading branch information
Showing
24 changed files
with
610 additions
and
885 deletions.
There are no files selected for viewing
This file contains 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 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,128 @@ | ||
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package basculehttp | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/xmidt-org/bascule/v1" | ||
) | ||
|
||
const ( | ||
// DefaultAuthorizationHeader is the default HTTP header used for authorization | ||
// tokens in an HTTP request. | ||
DefaultAuthorizationHeader = "Authorization" | ||
) | ||
|
||
var ( | ||
// ErrInvalidAuthorization indicates an authorization header value did not | ||
// correspond to the standard. | ||
ErrInvalidAuthorization = errors.New("invalidation authorization") | ||
|
||
// ErrMissingAuthorization indicates that no authorization header was | ||
// present in the source HTTP request. | ||
ErrMissingAuthorization = errors.New("missing authorization") | ||
) | ||
|
||
// fastIsSpace tests an ASCII byte to see if it's whitespace. | ||
// HTTP headers are restricted to US-ASCII, so we don't need | ||
// the full unicode stack. | ||
func fastIsSpace(b byte) bool { | ||
return b == ' ' || b == '\t' || b == '\n' || b == '\r' || b == '\v' || b == '\f' | ||
} | ||
|
||
// ParseAuthorization parses an authorization value typically passed in | ||
// the Authorization HTTP header. | ||
// | ||
// The required format is <scheme><single space><credential value>. This function | ||
// is strict: it requires no leading or trailing space and exactly (1) space as | ||
// a separator. If the raw value does not adhere to this format, ErrInvalidAuthorization | ||
// is returned. | ||
func ParseAuthorization(raw string) (s Scheme, v string, err error) { | ||
var scheme string | ||
var found bool | ||
scheme, v, found = strings.Cut(raw, " ") | ||
if found && len(scheme) > 0 && !fastIsSpace(v[0]) && !fastIsSpace(v[len(v)-1]) { | ||
s = Scheme(scheme) | ||
} else { | ||
err = ErrInvalidAuthorization | ||
} | ||
|
||
return | ||
} | ||
|
||
type AuthorizationParserOption interface { | ||
apply(*AuthorizationParser) error | ||
} | ||
|
||
type authorizationParserOptionFunc func(*AuthorizationParser) error | ||
|
||
func (apof authorizationParserOptionFunc) apply(ap *AuthorizationParser) error { return apof(ap) } | ||
|
||
func WithAuthorizationHeader(header string) AuthorizationParserOption { | ||
return authorizationParserOptionFunc(func(ap *AuthorizationParser) error { | ||
ap.header = header | ||
return nil | ||
}) | ||
} | ||
|
||
func WithScheme(scheme Scheme, parser bascule.TokenParser[string]) AuthorizationParserOption { | ||
return authorizationParserOptionFunc(func(ap *AuthorizationParser) error { | ||
// we want case-insensitive matches, so lowercase everything | ||
ap.parsers[scheme.lower()] = parser | ||
return nil | ||
}) | ||
} | ||
|
||
type AuthorizationParser struct { | ||
header string | ||
parsers map[Scheme]bascule.TokenParser[string] | ||
} | ||
|
||
func NewAuthorizationParser(opts ...AuthorizationParserOption) (*AuthorizationParser, error) { | ||
ap := &AuthorizationParser{ | ||
parsers: make(map[Scheme]bascule.TokenParser[string]), | ||
} | ||
|
||
for _, o := range opts { | ||
if err := o.apply(ap); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
if len(ap.header) == 0 { | ||
ap.header = DefaultAuthorizationHeader | ||
} | ||
|
||
return ap, nil | ||
} | ||
|
||
// Parse extracts the appropriate header, Authorization by default, and parses the | ||
// scheme and value. Schemes are case-insensitive, e.g. BASIC and Basic are the same scheme. | ||
// | ||
// If a token parser is registered for the given scheme, that token parser is invoked. | ||
// Otherwise, UnsupportedSchemeError is returned, indicating the scheme in question. | ||
func (ap *AuthorizationParser) Parse(ctx context.Context, source *http.Request) (bascule.Token, error) { | ||
authValue := source.Header.Get(ap.header) | ||
if len(authValue) == 0 { | ||
return nil, bascule.ErrMissingCredentials | ||
} | ||
|
||
scheme, value, err := ParseAuthorization(authValue) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
p, registered := ap.parsers[scheme.lower()] | ||
if !registered { | ||
return nil, &UnsupportedSchemeError{ | ||
Scheme: scheme, | ||
} | ||
} | ||
|
||
return p.Parse(ctx, value) | ||
} |
This file contains 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 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.