From 4fa7ea0b203743b93c87372385301f8594f87766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Tue, 7 May 2024 08:27:26 +0200 Subject: [PATCH] feat: add cli command to generate app token for user --- services/auth-app/pkg/command/create.go | 109 ++++++++++++++++++ services/auth-app/pkg/command/root.go | 1 + services/auth-app/pkg/revaconfig/config.go | 13 +++ services/gateway/pkg/config/config.go | 1 + .../pkg/config/defaults/defaultconfig.go | 1 + services/gateway/pkg/revaconfig/config.go | 2 + services/proxy/pkg/command/server.go | 2 +- services/proxy/pkg/middleware/app_auth.go | 10 +- 8 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 services/auth-app/pkg/command/create.go diff --git a/services/auth-app/pkg/command/create.go b/services/auth-app/pkg/command/create.go new file mode 100644 index 0000000000..04d892d6c5 --- /dev/null +++ b/services/auth-app/pkg/command/create.go @@ -0,0 +1,109 @@ +package command + +import ( + "context" + "fmt" + authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/auth/scope" + + applicationsv1beta1 "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1" + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/owncloud/ocis/v2/ocis-pkg/tracing" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/parser" + "github.com/urfave/cli/v2" + "google.golang.org/grpc/metadata" + "time" +) + +// Create is the entrypoint for the app auth create command +func Create(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "create", + Usage: "create an app auth token for a user", + Category: "maintenance", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "user-name", + Value: "", + Usage: "the user name", + Required: true, + }, + &cli.IntFlag{ + Name: "expiration", + Value: 72, + Usage: "expiration of the app password in hours", + Required: false, + }, + }, + Before: func(_ *cli.Context) error { + return configlog.ReturnError(parser.ParseConfig(cfg)) + }, + Action: func(c *cli.Context) error { + traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name) + if err != nil { + return err + } + + gatewaySelector, err := pool.GatewaySelector( + cfg.Reva.Address, + append( + cfg.Reva.GetRevaOptions(), + pool.WithRegistry(registry.GetRegistry()), + pool.WithTracerProvider(traceProvider), + )...) + if err != nil { + return err + } + + next, err := gatewaySelector.Next() + if err != nil { + return err + } + + userID := c.String("user-name") + ctx := context.Background() + authRes, err := next.Authenticate(ctx, &gatewayv1beta1.AuthenticateRequest{ + Type: "machine", + ClientId: "username:" + userID, + ClientSecret: cfg.Commons.MachineAuthAPIKey, + }) + if err != nil { + return err + } + granteeCtx := ctxpkg.ContextSetUser(context.Background(), &userpb.User{Id: authRes.GetUser().GetId()}) + granteeCtx = metadata.AppendToOutgoingContext(granteeCtx, ctxpkg.TokenHeader, authRes.GetToken()) + + exp := c.Int("expiration") + expiryDuration := time.Duration(exp) * time.Hour + scopes, err := scope.AddOwnerScope(map[string]*authpb.Scope{}) + if err != nil { + return err + } + + appPassword, err := next.GenerateAppPassword(granteeCtx, &applicationsv1beta1.GenerateAppPasswordRequest{ + TokenScope: scopes, + Label: "Generated via CLI", + Expiration: &typesv1beta1.Timestamp{ + Seconds: uint64(time.Now().Add(expiryDuration).Unix()), + }, + }) + if err != nil { + return err + } + + fmt.Printf("App password created for %s", authRes.GetUser().GetUsername()) + fmt.Println() + fmt.Printf(" password: %s", appPassword.GetAppPassword().GetPassword()) + fmt.Println() + + return nil + }, + } +} diff --git a/services/auth-app/pkg/command/root.go b/services/auth-app/pkg/command/root.go index 9563118240..f1ba83b36e 100644 --- a/services/auth-app/pkg/command/root.go +++ b/services/auth-app/pkg/command/root.go @@ -15,6 +15,7 @@ func GetCommands(cfg *config.Config) cli.Commands { Server(cfg), // interaction with this service + Create(cfg), // infos about this service Health(cfg), diff --git a/services/auth-app/pkg/revaconfig/config.go b/services/auth-app/pkg/revaconfig/config.go index 1a015fcd16..da75275314 100644 --- a/services/auth-app/pkg/revaconfig/config.go +++ b/services/auth-app/pkg/revaconfig/config.go @@ -1,11 +1,16 @@ package revaconfig import ( + "path/filepath" + + "github.com/owncloud/ocis/v2/ocis-pkg/config/defaults" "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" ) // AuthAppConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service. func AuthAppConfigFromStruct(cfg *config.Config) map[string]interface{} { + appAuthJSON := filepath.Join(defaults.BaseDataPath(), "appauth.json") + rcfg := map[string]interface{}{ "shared": map[string]interface{}{ "jwt_secret": cfg.TokenManager.JWTSecret, @@ -30,6 +35,14 @@ func AuthAppConfigFromStruct(cfg *config.Config) map[string]interface{} { }, }, }, + "applicationauth": map[string]interface{}{ + "driver": "json", + "drivers": map[string]interface{}{ + "json": map[string]interface{}{ + "file": appAuthJSON, + }, + }, + }, }, "interceptors": map[string]interface{}{ "prometheus": map[string]interface{}{ diff --git a/services/gateway/pkg/config/config.go b/services/gateway/pkg/config/config.go index 73b932de74..1d683de4b1 100644 --- a/services/gateway/pkg/config/config.go +++ b/services/gateway/pkg/config/config.go @@ -35,6 +35,7 @@ type Config struct { GroupsEndpoint string `yaml:"-"` PermissionsEndpoint string `yaml:"-"` SharingEndpoint string `yaml:"-"` + AuthAppEndpoint string `yaml:"-"` AuthBasicEndpoint string `yaml:"-"` AuthBearerEndpoint string `yaml:"-"` AuthMachineEndpoint string `yaml:"-"` diff --git a/services/gateway/pkg/config/defaults/defaultconfig.go b/services/gateway/pkg/config/defaults/defaultconfig.go index b8bea1a4bc..5d003bb379 100644 --- a/services/gateway/pkg/config/defaults/defaultconfig.go +++ b/services/gateway/pkg/config/defaults/defaultconfig.go @@ -52,6 +52,7 @@ func DefaultConfig() *config.Config { FrontendPublicURL: "https://localhost:9200", AppRegistryEndpoint: "com.owncloud.api.app-registry", + AuthAppEndpoint: "com.owncloud.api.auth-app", AuthBasicEndpoint: "com.owncloud.api.auth-basic", AuthMachineEndpoint: "com.owncloud.api.auth-machine", AuthServiceEndpoint: "com.owncloud.api.auth-service", diff --git a/services/gateway/pkg/revaconfig/config.go b/services/gateway/pkg/revaconfig/config.go index f4b5e841ef..035ae05bf6 100644 --- a/services/gateway/pkg/revaconfig/config.go +++ b/services/gateway/pkg/revaconfig/config.go @@ -37,6 +37,7 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i // TODO build services dynamically "services": map[string]interface{}{ "gateway": map[string]interface{}{ + "applicationauthsvc": cfg.AuthAppEndpoint, // registries are located on the gateway "authregistrysvc": cfg.Reva.Address, "storageregistrysvc": cfg.Reva.Address, @@ -89,6 +90,7 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i "drivers": map[string]interface{}{ "static": map[string]interface{}{ "rules": map[string]interface{}{ + "appauth": cfg.AuthAppEndpoint, "basic": cfg.AuthBasicEndpoint, "machine": cfg.AuthMachineEndpoint, "publicshares": cfg.StoragePublicLinkEndpoint, diff --git a/services/proxy/pkg/command/server.go b/services/proxy/pkg/command/server.go index 8793651038..9bf9245acd 100644 --- a/services/proxy/pkg/command/server.go +++ b/services/proxy/pkg/command/server.go @@ -364,7 +364,7 @@ func loadMiddlewares(logger log.Logger, cfg *config.Config, userInfoCache, signi middleware.CredentialsByUserAgent(cfg.AuthMiddleware.CredentialsByUserAgent), middleware.Logger(logger), middleware.OIDCIss(cfg.OIDC.Issuer), - middleware.EnableBasicAuth(cfg.EnableBasicAuth), + middleware.EnableBasicAuth(true), middleware.TraceProvider(traceProvider), ), middleware.AccountResolver( diff --git a/services/proxy/pkg/middleware/app_auth.go b/services/proxy/pkg/middleware/app_auth.go index f03791b299..815e9a4575 100644 --- a/services/proxy/pkg/middleware/app_auth.go +++ b/services/proxy/pkg/middleware/app_auth.go @@ -4,6 +4,8 @@ import ( "net/http" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" ) @@ -31,6 +33,7 @@ func (m AppAuthAuthenticator) Authenticate(r *http.Request) (*http.Request, bool if err != nil { return nil, false } + authenticateResponse, err := next.Authenticate(r.Context(), &gateway.AuthenticateRequest{ Type: "appauth", ClientId: username, @@ -39,7 +42,12 @@ func (m AppAuthAuthenticator) Authenticate(r *http.Request) (*http.Request, bool if err != nil { return nil, false } - r.Header.Add(_headerRevaAccessToken, authenticateResponse.GetToken()) + if authenticateResponse.GetStatus().GetCode() != cs3rpc.Code_CODE_OK { + // TODO: log??? + return nil, false + } + + r.Header.Set(revactx.TokenHeader, authenticateResponse.GetToken()) return r, true }