Skip to content
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

Prepare model and config for more token type validators like OIDC #56

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/app/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

type TokenGetter interface {
GetToken(ctx context.Context, tokenValue string) (*model.Token, error)
GetStaticTokenValidation(ctx context.Context, tokenValue string) (*model.StaticTokenValidation, error)
}

//go:generate mockery --case underscore --output authmock --outpkg authmock --name TokenGetter
Expand Down Expand Up @@ -68,7 +68,7 @@ func (s Service) Authenticate(ctx context.Context, req AuthenticateRequest) (res
logger := s.logger.WithValues(log.Kv{"url": req.Review.HTTPURL, "method": req.Review.HTTPMethod})

// Get token and its properties.
token, err := s.tokenGetter.GetToken(ctx, req.Review.Token)
token, err := s.tokenGetter.GetStaticTokenValidation(ctx, req.Review.Token)
if err != nil {
if errors.Is(err, internalerrors.ErrNotFound) {
logger.Infof("Unknown token")
Expand Down
32 changes: 19 additions & 13 deletions internal/app/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestServiceAuth(t *testing.T) {

"A token review with fails while getting the token it should fail.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "sometoken").Once().Return(nil, fmt.Errorf("something"))
mtg.On("GetStaticTokenValidation", mock.Anything, "sometoken").Once().Return(nil, fmt.Errorf("something"))
},
req: auth.AuthenticateRequest{Review: model.TokenReview{
Token: "sometoken",
Expand All @@ -45,7 +45,7 @@ func TestServiceAuth(t *testing.T) {

"A token review with a valid token that is missing, should not return as authenticated.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "missing").Once().Return(nil, internalerrors.ErrNotFound)
mtg.On("GetStaticTokenValidation", mock.Anything, "missing").Once().Return(nil, internalerrors.ErrNotFound)
},
req: auth.AuthenticateRequest{Review: model.TokenReview{
Token: "missing",
Expand All @@ -55,9 +55,11 @@ func TestServiceAuth(t *testing.T) {

"A token review that is disabled should be invalid.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "token0").Once().Return(&model.Token{
Value: "token0",
Disable: true,
mtg.On("GetStaticTokenValidation", mock.Anything, "token0").Once().Return(&model.StaticTokenValidation{
Value: "token0",
Common: model.TokenCommon{
Disable: true,
},
}, nil)
},
req: auth.AuthenticateRequest{Review: model.TokenReview{
Expand All @@ -68,7 +70,7 @@ func TestServiceAuth(t *testing.T) {

"A token review that has expired should be invalid.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "token0").Once().Return(&model.Token{
mtg.On("GetStaticTokenValidation", mock.Anything, "token0").Once().Return(&model.StaticTokenValidation{
Value: "token0",
ExpiresAt: time.Now().Add(-24 * time.Hour),
}, nil)
Expand All @@ -81,9 +83,11 @@ func TestServiceAuth(t *testing.T) {

"A token review with an invalid URL should be invalid.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "token0").Once().Return(&model.Token{
Value: "token0",
AllowedURL: regexp.MustCompile("https://something.com/.*"),
mtg.On("GetStaticTokenValidation", mock.Anything, "token0").Once().Return(&model.StaticTokenValidation{
Value: "token0",
Common: model.TokenCommon{
AllowedURL: regexp.MustCompile("https://something.com/.*"),
},
}, nil)
},
req: auth.AuthenticateRequest{Review: model.TokenReview{
Expand All @@ -95,9 +99,11 @@ func TestServiceAuth(t *testing.T) {

"A token review with an invalid method should be invalid.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "token0").Once().Return(&model.Token{
Value: "token0",
AllowedMethod: regexp.MustCompile("POST"),
mtg.On("GetStaticTokenValidation", mock.Anything, "token0").Once().Return(&model.StaticTokenValidation{
Value: "token0",
Common: model.TokenCommon{
AllowedMethod: regexp.MustCompile("POST"),
},
}, nil)
},
req: auth.AuthenticateRequest{Review: model.TokenReview{
Expand All @@ -109,7 +115,7 @@ func TestServiceAuth(t *testing.T) {

"A token review that is valid, should be authenticated.": {
mock: func(mtg *authmock.TokenGetter) {
mtg.On("GetToken", mock.Anything, "token0").Once().Return(&model.Token{
mtg.On("GetStaticTokenValidation", mock.Anything, "token0").Once().Return(&model.StaticTokenValidation{
Value: "token0",
ClientID: "client0",
}, nil)
Expand Down
14 changes: 7 additions & 7 deletions internal/app/auth/authmock/token_getter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 14 additions & 14 deletions internal/app/auth/token_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ type reviewResult struct {

// Authenticater knows how to authenticate.
type authenticater interface {
Authenticate(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error)
Authenticate(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error)
}

type authenticaterFunc func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error)
type authenticaterFunc func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error)

func (a authenticaterFunc) Authenticate(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
func (a authenticaterFunc) Authenticate(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
return a(ctx, r, t)
}

func newAuthenticaterChain(auths ...authenticater) authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
var res *reviewResult
var err error
for _, a := range auths {
Expand All @@ -52,7 +52,7 @@ func newAuthenticaterChain(auths ...authenticater) authenticater {
}

func newTokenExistAuthenticator() authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
if r.Token == t.Value {
return &reviewResult{Valid: true}, nil
}
Expand All @@ -62,7 +62,7 @@ func newTokenExistAuthenticator() authenticater {
}

func newNotExpiredAuthenticator() authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
if t.ExpiresAt.IsZero() {
return &reviewResult{Valid: true}, nil
}
Expand All @@ -76,12 +76,12 @@ func newNotExpiredAuthenticator() authenticater {
}

func newValidMethodAuthenticator() authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
if t.AllowedMethod == nil {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
if t.Common.AllowedMethod == nil {
return &reviewResult{Valid: true}, nil
}

if t.AllowedMethod.MatchString(r.HTTPMethod) {
if t.Common.AllowedMethod.MatchString(r.HTTPMethod) {
return &reviewResult{Valid: true}, nil
}

Expand All @@ -90,12 +90,12 @@ func newValidMethodAuthenticator() authenticater {
}

func newValidURLAuthenticator() authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
if t.AllowedURL == nil {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
if t.Common.AllowedURL == nil {
return &reviewResult{Valid: true}, nil
}

if t.AllowedURL.MatchString(r.HTTPURL) {
if t.Common.AllowedURL.MatchString(r.HTTPURL) {
return &reviewResult{Valid: true}, nil
}

Expand All @@ -104,8 +104,8 @@ func newValidURLAuthenticator() authenticater {
}

func newDisabledAuthenticator() authenticater {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.Token) (*reviewResult, error) {
if !t.Disable {
return authenticaterFunc(func(ctx context.Context, r model.TokenReview, t model.StaticTokenValidation) (*reviewResult, error) {
if !t.Common.Disable {
return &reviewResult{Valid: true}, nil
}

Expand Down
14 changes: 9 additions & 5 deletions internal/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import (
"time"
)

// Token represents an auth token that can be used to validate an authentication.
// StaticTokenValidation represents an auth static token information that can be used to validate an authentication.
// Token value generation example: `openssl rand -base64 32`.
type Token struct {
Value string
ClientID string
type StaticTokenValidation struct {
Value string
ClientID string
ExpiresAt time.Time
Common TokenCommon
}

type TokenCommon struct {
Disable bool
ExpiresAt time.Time
AllowedURL *regexp.Regexp
AllowedMethod *regexp.Regexp
}
Expand Down
14 changes: 8 additions & 6 deletions internal/storage/memory/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
apiv1 "github.com/slok/simple-ingress-external-auth/pkg/api/v1"
)

func mapJSONV1ToModel(data string) (map[string]model.Token, error) {
func mapJSONV1ToModel(data string) (map[string]model.StaticTokenValidation, error) {
// Substitute env vars in the required strings.
envedData, err := envsubst.EvalEnv(data)
if err != nil {
Expand All @@ -35,7 +35,7 @@ func mapJSONV1ToModel(data string) (map[string]model.Token, error) {
}

// Map.
tokens := map[string]model.Token{}
tokens := map[string]model.StaticTokenValidation{}
for _, t := range c1.Tokens {
if t.Value == "" {
return nil, fmt.Errorf("token value can't be empty")
Expand All @@ -46,27 +46,29 @@ func mapJSONV1ToModel(data string) (map[string]model.Token, error) {
expiresAt = *t.ExpiresAt
}

token := model.Token{
token := model.StaticTokenValidation{
Value: t.Value,
ClientID: t.ClientID,
Disable: t.Disable,
ExpiresAt: expiresAt,
Common: model.TokenCommon{
Disable: t.Disable,
},
}

if t.AllowedMethodRegex != "" {
r, err := regexp.Compile(t.AllowedMethodRegex)
if err != nil {
return nil, fmt.Errorf("could not compile %s regex: %w", t.AllowedMethodRegex, err)
}
token.AllowedMethod = r
token.Common.AllowedMethod = r
}

if t.AllowedURLRegex != "" {
r, err := regexp.Compile(t.AllowedURLRegex)
if err != nil {
return nil, fmt.Errorf("could not compile %s regex: %w", t.AllowedURLRegex, err)
}
token.AllowedURL = r
token.Common.AllowedURL = r
}

// Check same token is not twice.
Expand Down
6 changes: 3 additions & 3 deletions internal/storage/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

type TokenRepository struct {
tokens map[string]model.Token
tokens map[string]model.StaticTokenValidation
}

func NewTokenRepository(logger log.Logger, config string) (*TokenRepository, error) {
Expand All @@ -19,12 +19,12 @@ func NewTokenRepository(logger log.Logger, config string) (*TokenRepository, err
return nil, err
}

logger.WithValues(log.Kv{"svc": "memory.TokenRepository", "tokens": len(tokens)}).Infof("Tokens loaded")
logger.WithValues(log.Kv{"svc": "memory.TokenRepository", "tokens": len(tokens)}).Infof("Token validations loaded")

return &TokenRepository{tokens: tokens}, nil
}

func (t TokenRepository) GetToken(ctx context.Context, tokenValue string) (*model.Token, error) {
func (t TokenRepository) GetStaticTokenValidation(ctx context.Context, tokenValue string) (*model.StaticTokenValidation, error) {
token, ok := t.tokens[tokenValue]
if !ok {
return nil, fmt.Errorf("token not found: %w", internalerrors.ErrNotFound)
Expand Down
Loading