diff --git a/cmd/microauth2sqld/main.go b/cmd/microauth2sqld/main.go index 8c1cea0..3c5b93c 100644 --- a/cmd/microauth2sqld/main.go +++ b/cmd/microauth2sqld/main.go @@ -237,7 +237,7 @@ func main() { os.Exit(0) } - if err := auth2ClientReg.Init(c, srv); err != nil { + if err := auth2ClientReg.Init(auth2.CliContext(c), auth2.Service(srv), auth2.Logrus(ilogger.Logrus())); err != nil { ilogger.Logrus().Fatal(err) } diff --git a/go.mod b/go.mod index 76cc189..ea6d941 100644 --- a/go.mod +++ b/go.mod @@ -80,12 +80,13 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 // indirect + golang.org/x/net v0.0.0-20220921203646-d300de134e69 // indirect golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect golang.org/x/tools v0.1.12 // indirect - google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 // indirect + google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect google.golang.org/grpc v1.49.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 85042df..20f70bd 100644 --- a/go.sum +++ b/go.sum @@ -1259,7 +1259,6 @@ go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -1421,8 +1420,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 h1:asZqf0wXastQr+DudYagQS8uBO8bHKeYD1vbAvGmFL8= -golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= +golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1609,8 +1608,9 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1816,8 +1816,8 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 h1:mmbq5q8M1t7dhkLw320YK4PsOXm6jdnUAkErImaIqOg= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/noop.go b/noop.go index 3810499..dd04089 100644 --- a/noop.go +++ b/noop.go @@ -6,7 +6,6 @@ import ( "github.com/google/uuid" "github.com/urfave/cli/v2" - "go-micro.dev/v4" "go-micro.dev/v4/server" ) @@ -29,7 +28,7 @@ func (p *noopClientPlugin) MergeFlags(flags []cli.Flag) []cli.Flag { return flags } -func (p *noopClientPlugin) Init(cli *cli.Context, service micro.Service) error { +func (p *noopClientPlugin) Init(opts ...InitOption) error { return nil } @@ -70,7 +69,7 @@ func (p *noopRouterPlugin) MergeFlags(flags []cli.Flag) []cli.Flag { return flags } -func (p *noopRouterPlugin) Init(cli *cli.Context, service micro.Service) error { +func (p *noopRouterPlugin) Init(opts ...InitOption) error { return nil } diff --git a/options.go b/options.go new file mode 100644 index 0000000..225ae9e --- /dev/null +++ b/options.go @@ -0,0 +1,48 @@ +package auth2 + +import ( + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go-micro.dev/v4/errors" +) + +type InitOptions struct { + CliContext *cli.Context + Service micro.Service + Logrus *logrus.Logger +} + +type InitOption func(o *InitOptions) + +func CliContext(n *cli.Context) InitOption { + return func(o *InitOptions) { + o.CliContext = n + } +} + +func Service(n micro.Service) InitOption { + return func(o *InitOptions) { + o.Service = n + } +} + +func Logrus(n *logrus.Logger) InitOption { + return func(o *InitOptions) { + o.Logrus = n + } +} + +func NewInitOptions(opts ...InitOption) (InitOptions, error) { + options := InitOptions{} + for _, o := range opts { + o(&options) + } + + // Make CliContext() required + if options.CliContext == nil { + return options, errors.InternalServerError("auth2.NewInitOptions:no cli.Context", "no cli.Context hase been given") + } + + return options, nil +} diff --git a/plugin.go b/plugin.go index 19d1e45..885b201 100644 --- a/plugin.go +++ b/plugin.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/urfave/cli/v2" - "go-micro.dev/v4" "go-micro.dev/v4/server" ) @@ -17,7 +16,7 @@ type registryFuncs interface { MergeFlags(flags []cli.Flag) []cli.Flag // Init should be executed in micro.Init - Init(cli *cli.Context, service micro.Service) error + Init(opts ...InitOption) error // Stop should be executed after service.Run() Stop() error diff --git a/plugins/client/jwt/jwt.go b/plugins/client/jwt/jwt.go index c08f764..5bd0797 100644 --- a/plugins/client/jwt/jwt.go +++ b/plugins/client/jwt/jwt.go @@ -11,7 +11,6 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/urfave/cli/v2" - "go-micro.dev/v4" "go-micro.dev/v4/metadata" "go-micro.dev/v4/server" "jochum.dev/jo-micro/auth2" @@ -61,21 +60,26 @@ func (p *jwtPlugin) MergeFlags(flags []cli.Flag) []cli.Flag { ) } -func (p *jwtPlugin) Init(cli *cli.Context, service micro.Service) error { - if len(cli.String("auth2_jwt_pub_key")) < 1 || len(cli.String("auth2_jwt_priv_key")) < 1 { +func (p *jwtPlugin) Init(opts ...auth2.InitOption) error { + options, err := auth2.NewInitOptions(opts...) + if err != nil { + return err + } + + if len(options.CliContext.String("auth2_jwt_pub_key")) < 1 || len(options.CliContext.String("auth2_jwt_priv_key")) < 1 { return errors.New("you must provide auth2_jwt_(priv|pub)_key") } - if cli.StringSlice("auth2_jwt_audience") == nil { + if options.CliContext.StringSlice("auth2_jwt_audience") == nil { return errors.New("MICRO_AUTH2_JWT_AUDIENCES must be given") } - pub, priv, err := sjwt.DecodeKeyPair(cli.String("auth2_jwt_pub_key"), cli.String("auth2_jwt_priv_key")) + pub, priv, err := sjwt.DecodeKeyPair(options.CliContext.String("auth2_jwt_pub_key"), options.CliContext.String("auth2_jwt_priv_key")) if err != nil { return err } - p.audiences = cli.StringSlice("auth2_jwt_audience") + p.audiences = options.CliContext.StringSlice("auth2_jwt_audience") p.pubKey = pub p.privKey = priv diff --git a/plugins/router/jwt/jwt.go b/plugins/router/jwt/jwt.go index 45adfdc..88b0f14 100644 --- a/plugins/router/jwt/jwt.go +++ b/plugins/router/jwt/jwt.go @@ -5,15 +5,16 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" - "errors" "fmt" "net/http" "strings" "github.com/golang-jwt/jwt/v4" + "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" - "go-micro.dev/v4" + "go-micro.dev/v4/errors" "go-micro.dev/v4/metadata" + "jochum.dev/jo-micro/auth2" auth "jochum.dev/jo-micro/auth2" "jochum.dev/jo-micro/auth2/shared/sjwt" "jochum.dev/jo-micro/auth2/shared/sutil" @@ -28,7 +29,16 @@ func newJWTPlugin() auth.RouterPlugin { } type jwtPlugin struct { - pubKey any + pubKey any + options auth2.InitOptions +} + +func (p *jwtPlugin) logrus() *logrus.Logger { + if p.options.Logrus == nil { + return logrus.StandardLogger() + } + + return p.options.Logrus } func (p *jwtPlugin) String() string { @@ -43,18 +53,23 @@ func (p *jwtPlugin) MergeFlags(flags []cli.Flag) []cli.Flag { }) } -func (p *jwtPlugin) Init(cli *cli.Context, service micro.Service) error { - if len(cli.String("auth2_jwt_pub_key")) < 1 { - return errors.New("you must provide auth2_jwt_pub_key") +func (p *jwtPlugin) Init(opts ...auth2.InitOption) error { + options, err := auth2.NewInitOptions(opts...) + if err != nil { + return err + } + + if len(options.CliContext.String("auth2_jwt_pub_key")) < 1 { + return errors.InternalServerError("auth2/plugins/router/jwt.Init:No auth2_jwt_pub_key", "you must provide auth2_jwt_pub_key") } - aPub, err := base64.StdEncoding.DecodeString(cli.String("auth2_jwt_pub_key")) + aPub, err := base64.StdEncoding.DecodeString(options.CliContext.String("auth2_jwt_pub_key")) if err != nil { return err } block, _ := pem.Decode(aPub) if block == nil { - return errors.New("failed to parse PEM block containing the key") + return errors.InternalServerError("auth2/plugins/router/jwt.Init:PEM parsing", "failed to parse PEM block containing the key") } pub, err := x509.ParsePKIXPublicKey(block.Bytes) @@ -76,11 +91,12 @@ func (p *jwtPlugin) Health(ctx context.Context) (string, error) { } func (p *jwtPlugin) Inspect(r *http.Request) (*auth.User, error) { - if h := r.Header.Get("Authorization"); len(h) > 0 { - return nil, errors.New("failed to get Authorization header from context") + if _, ok := r.Header["Authorization"]; !ok { + p.logrus().WithField("headers", r.Header).Debug("empty or no Authorization header in request") + return nil, errors.InternalServerError("auth2/plugins/router/jwt.Inspect", "empty or no Authorization header in request") } - aTokenString, _, err := sutil.ExtractToken(r.Header.Get("Authorization")) + aTokenString, _, err := sutil.ExtractToken(r.Header["Authorization"][0]) if err != nil { return nil, err } @@ -105,7 +121,7 @@ func (p *jwtPlugin) Inspect(r *http.Request) (*auth.User, error) { } func (p *jwtPlugin) ForwardContext(r *http.Request, ctx context.Context) (context.Context, error) { - _, err := p.Inspect(r) + u, err := p.Inspect(r) if err != nil { return ctx, err } @@ -118,5 +134,7 @@ func (p *jwtPlugin) ForwardContext(r *http.Request, ctx context.Context) (contex md["X-Fowarded-For"] = v } + p.logrus().WithField("username", u.Metadata["Subject"]).Trace("Forwarding user") + return metadata.MergeContext(ctx, md, true), nil } diff --git a/plugins/verifier/endpointroles/verifier.go b/plugins/verifier/endpointroles/verifier.go index 0d7f773..9112f93 100644 --- a/plugins/verifier/endpointroles/verifier.go +++ b/plugins/verifier/endpointroles/verifier.go @@ -11,21 +11,24 @@ import ( ) type EndpointRolesVerifier struct { - rules map[string]Rule - options Options + rules map[string]Rule + endpointnames []string + options Options } func NewVerifier(opts ...Option) *EndpointRolesVerifier { options := NewOptions(opts...) return &EndpointRolesVerifier{ - rules: make(map[string]Rule, 0), - options: options, + rules: make(map[string]Rule, 0), + endpointnames: []string{}, + options: options, } } func (v *EndpointRolesVerifier) AddRules(rules ...Rule) { for _, rule := range rules { + v.endpointnames = append(v.endpointnames, rule.Endpoint) v.rules[rule.Endpoint] = rule } } @@ -51,16 +54,16 @@ func (v *EndpointRolesVerifier) Verify(ctx context.Context, u *auth2.User, req s } if v.options.DefaultDeny { - v.logrus().WithField("endpoint", req.Endpoint()).Debug("DefaultDeny: not in RolesAllow/Deny") - return errors.Unauthorized("auth2/plugins/verifier/endpointroles/EndpointRolesVerifier.Verify|No matching Role", "Unauthorized") + v.logrus().WithField("endpoint", req.Endpoint()).WithField("user_roles", u.Roles).WithField("roles_allow", ep.RolesAllow).Debug("DefaultDeny: No matching role") + return errors.Unauthorized("auth2/plugins/verifier/endpointroles/EndpointRolesVerifier.Verify|No matching role", "Unauthorized") } } if !v.options.DefaultDeny { - v.logrus().WithField("endpoint", req.Endpoint()).Trace("DefaultAllow: no rule") + v.logrus().WithField("endpoint", req.Endpoint()).WithField("endpoints", v.endpointnames).Trace("DefaultAllow: No rule") return nil } - v.logrus().WithField("endpoint", req.Endpoint()).Debug("DefaultDeny: no rule") - return errors.Unauthorized("auth2/plugins/verifier/endpointroles/EndpointRolesVerifier.Verify|No rule for EP", "Unauthorized") + v.logrus().WithField("endpoint", req.Endpoint()).WithField("endpoints", v.endpointnames).Debug("DefaultDeny: no rule") + return errors.Unauthorized("auth2/plugins/verifier/endpointroles/EndpointRolesVerifier.Verify|No rule", "Unauthorized") } diff --git a/registry.go b/registry.go index 9e78fd7..fe86bc4 100644 --- a/registry.go +++ b/registry.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/urfave/cli/v2" - "go-micro.dev/v4" "go-micro.dev/v4/errors" "go-micro.dev/v4/server" "jochum.dev/jo-micro/auth2/shared/sutil" @@ -76,9 +75,14 @@ func (r *AuthRegistry[T]) Plugin() T { } // Init should be executed in micro.Init -func (r *AuthRegistry[T]) Init(cli *cli.Context, service micro.Service) error { +func (r *AuthRegistry[T]) Init(opts ...InitOption) error { + options, err := NewInitOptions(opts...) + if err != nil { + return err + } + if r.forcedPlugin == "" { - plugin := cli.String(fmt.Sprintf("auth2_%s", r.kind)) + plugin := options.CliContext.String(fmt.Sprintf("auth2_%s", r.kind)) m, ok := r.plugins[plugin] if !ok { return fmt.Errorf("unknown MICRO_AUTH2_%s plugin '%s'", strings.ToUpper(r.kind), plugin) @@ -88,7 +92,7 @@ func (r *AuthRegistry[T]) Init(cli *cli.Context, service micro.Service) error { } m2, _ := any(r.plugin).(registryFuncs) - return m2.Init(cli, service) + return m2.Init(opts...) } // Stop should be executed after service.Run()