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

Implement admin authenticator #5

Merged
merged 11 commits into from
Nov 13, 2023
60 changes: 60 additions & 0 deletions internal/authenticator/admin_authenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package authenticator

import (
"fmt"

"github.com/golang-jwt/jwt/v5"
"github.com/snapp-incubator/soteria/internal/config"
"github.com/snapp-incubator/soteria/pkg/acl"
)

// AdminAuthenticator is responsible for Acl/Auth/Token of the internal system users,
// these users have admin access.
type AdminAuthenticator struct {
Key any
Company string
JwtConfig config.Jwt
Parser *jwt.Parser
}

// Auth check user authentication by checking the user's token
// isSuperuser is a flag that authenticator set it true when credentials is related to a superuser.
func (a AdminAuthenticator) Auth(tokenString string) error {
_, err := a.Parser.Parse(tokenString, func(
token *jwt.Token,
) (interface{}, error) {
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, ErrInvalidClaims
}
if claims[a.JwtConfig.IssName] == nil {
return nil, ErrIssNotFound
}

Check warning on line 32 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L22-L32

Added lines #L22 - L32 were not covered by tests

return a.Key, nil

Check warning on line 34 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L34

Added line #L34 was not covered by tests
})
if err != nil {
return fmt.Errorf("token is invalid: %w", err)
}

Check warning on line 38 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L36-L38

Added lines #L36 - L38 were not covered by tests

return nil

Check warning on line 40 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L40

Added line #L40 was not covered by tests
}

// ACL check a system user access to a topic.
// because we returns is-admin: true, this endpoint shouldn't
// be called.
func (a AdminAuthenticator) ACL(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@1995parham In long term view we can think about restricting services on their topics.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right Ahmad jan, EMQ has a concept which creates tenants for you by prefixing topics. We can use this feature for supporting different vendors but right now we need shared topics. For example Snapp Box want to read the Snapp events this means we need to validate Snapp Box tokens for Snapp topics.

_ acl.AccessType,
_ string,
_ string,
) (bool, error) {
return true, nil

Check warning on line 51 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L50-L51

Added lines #L50 - L51 were not covered by tests
}

func (a AdminAuthenticator) ValidateAccessType(_ acl.AccessType) bool {
return true

Check warning on line 55 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L54-L55

Added lines #L54 - L55 were not covered by tests
}

func (a AdminAuthenticator) GetCompany() string {
return a.Company

Check warning on line 59 in internal/authenticator/admin_authenticator.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/admin_authenticator.go#L58-L59

Added lines #L58 - L59 were not covered by tests
}
23 changes: 19 additions & 4 deletions internal/authenticator/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ValidatorConfig config.Validator
}

// nolint: funlen
func (b Builder) Authenticators() map[string]Authenticator {
all := make(map[string]Authenticator)

Expand All @@ -30,11 +31,12 @@
b.Logger.Fatal("cannot create hash-id manager", zap.Error(err))
}

client := validator.New(b.ValidatorConfig.URL, b.ValidatorConfig.Timeout)

var auth Authenticator

if vendor.UseValidator {
switch {
case vendor.UseValidator:
client := validator.New(b.ValidatorConfig.URL, b.ValidatorConfig.Timeout)

Check warning on line 39 in internal/authenticator/builder.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/builder.go#L36-L39

Added lines #L36 - L39 were not covered by tests
auth = &AutoAuthenticator{
AllowedAccessTypes: allowedAccessTypes,
Company: vendor.Company,
Expand All @@ -50,7 +52,20 @@
Validator: client,
Parser: jwt.NewParser(),
}
} else {
case vendor.IsInternal:
if _, ok := vendor.Keys["system"]; !ok || len(vendor.Keys) != 1 {
b.Logger.Fatal("admin authenticator supports only one key named system")
}

Check warning on line 58 in internal/authenticator/builder.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/builder.go#L55-L58

Added lines #L55 - L58 were not covered by tests

keys := b.GenerateKeys(vendor.Jwt.SigningMethod, vendor.Keys)

auth = &AdminAuthenticator{
Key: keys["system"],
Company: vendor.Company,
JwtConfig: vendor.Jwt,
Parser: jwt.NewParser(),
}
default:

Check warning on line 68 in internal/authenticator/builder.go

View check run for this annotation

Codecov / codecov/patch

internal/authenticator/builder.go#L60-L68

Added lines #L60 - L68 were not covered by tests
keys := b.GenerateKeys(vendor.Jwt.SigningMethod, vendor.Keys)

auth = &ManualAuthenticator{
Expand Down
11 changes: 7 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ type (
IssEntityMap map[string]string `json:"iss_entity_map,omitempty" koanf:"iss_entity_map"`
IssPeerMap map[string]string `json:"iss_peer_map,omitempty" koanf:"iss_peer_map"`
Jwt Jwt `json:"jwt,omitempty" koanf:"jwt"`
// by setting do validate to false we don't validate the jwt token and deligate
// it into a function.
UseValidator bool `json:"use_validator,omitempty" koanf:"use_validator"`
HashIDMap map[string]topics.HashData `json:"hash_id_map,omitempty" koanf:"hashid_map"`
// by setting use validator to true we don't validate the jwt token and deligate
// it into an http client.
UseValidator bool `json:"use_validator,omitempty" koanf:"use_validator"`
// by setting is internal to true we use internal authenticator which provides admin access
// on the authentication method.
IsInternal bool `json:"is_internal,omitempty" koanf:"is_internal"`
HashIDMap map[string]topics.HashData `json:"hash_id_map,omitempty" koanf:"hashid_map"`
}

Jwt struct {
Expand Down
1 change: 1 addition & 0 deletions internal/config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Default() Config {
func SnappVendor() Vendor {
return Vendor{
UseValidator: false,
IsInternal: false,
AllowedAccessTypes: []string{
"pub",
"sub",
Expand Down
Loading