From 1eb66eb18c057b451f03b0c47d77e5e2505e671a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Mon, 6 May 2024 16:04:27 +0200 Subject: [PATCH] feat: add REVA appauth as oCIS service --- docs/services/general-info/port-ranges.md | 2 +- ocis-pkg/config/config.go | 2 + ocis-pkg/config/defaultconfig.go | 2 + ocis/pkg/command/services.go | 12 +- ocis/pkg/runtime/service/service.go | 6 + services/auth-app/Makefile | 37 +++++++ services/auth-app/README.md | 3 + services/auth-app/cmd/auth-app/main.go | 14 +++ services/auth-app/pkg/command/health.go | 54 +++++++++ services/auth-app/pkg/command/root.go | 34 ++++++ services/auth-app/pkg/command/server.go | 104 ++++++++++++++++++ services/auth-app/pkg/command/version.go | 50 +++++++++ services/auth-app/pkg/config/config.go | 55 +++++++++ .../pkg/config/defaults/defaultconfig.go | 83 ++++++++++++++ services/auth-app/pkg/config/parser/parse.go | 42 +++++++ services/auth-app/pkg/config/reva.go | 6 + services/auth-app/pkg/config/tracing.go | 21 ++++ services/auth-app/pkg/logging/logging.go | 17 +++ services/auth-app/pkg/revaconfig/config.go | 43 ++++++++ services/auth-app/pkg/server/debug/option.go | 50 +++++++++ services/auth-app/pkg/server/debug/server.go | 63 +++++++++++ 21 files changed, 696 insertions(+), 4 deletions(-) create mode 100644 services/auth-app/Makefile create mode 100644 services/auth-app/README.md create mode 100644 services/auth-app/cmd/auth-app/main.go create mode 100644 services/auth-app/pkg/command/health.go create mode 100644 services/auth-app/pkg/command/root.go create mode 100644 services/auth-app/pkg/command/server.go create mode 100644 services/auth-app/pkg/command/version.go create mode 100644 services/auth-app/pkg/config/config.go create mode 100644 services/auth-app/pkg/config/defaults/defaultconfig.go create mode 100644 services/auth-app/pkg/config/parser/parse.go create mode 100644 services/auth-app/pkg/config/reva.go create mode 100644 services/auth-app/pkg/config/tracing.go create mode 100644 services/auth-app/pkg/logging/logging.go create mode 100644 services/auth-app/pkg/revaconfig/config.go create mode 100644 services/auth-app/pkg/server/debug/option.go create mode 100644 services/auth-app/pkg/server/debug/server.go diff --git a/docs/services/general-info/port-ranges.md b/docs/services/general-info/port-ranges.md index d36dd9a62..ae027bbee 100644 --- a/docs/services/general-info/port-ranges.md +++ b/docs/services/general-info/port-ranges.md @@ -60,7 +60,7 @@ We also suggest using the last port in your extensions' range as a debug/metrics | 9230-9234 | [nats]({{< ref "../nats/_index.md" >}}) | | 9235-9239 | [idm]({{< ref "../idm/_index.md" >}}) | | 9240-9244 | [app-registry]({{< ref "../app-registry/_index.md" >}}) | -| 9245-9249 | FREE | +| 9245-9249 | [auth-app]({{< ref "../auth-app/_index.md" >}}) | | 9250-9254 | [ocis server (runtime)](https://github.com/owncloud/ocis/tree/master/ocis/pkg/runtime) | | 9255-9259 | [postprocessing]({{< ref "../postprocessing/_index.md" >}}) | | 9260-9264 | [clientlog]({{< ref "../clientlog/_index.md" >}}) | diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go index 245e8ceb0..79926807f 100644 --- a/ocis-pkg/config/config.go +++ b/ocis-pkg/config/config.go @@ -7,6 +7,7 @@ import ( appProvider "github.com/owncloud/ocis/v2/services/app-provider/pkg/config" appRegistry "github.com/owncloud/ocis/v2/services/app-registry/pkg/config" audit "github.com/owncloud/ocis/v2/services/audit/pkg/config" + authapp "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config" authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config" authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/config" @@ -86,6 +87,7 @@ type Config struct { AppProvider *appProvider.Config `yaml:"app_provider"` AppRegistry *appRegistry.Config `yaml:"app_registry"` Audit *audit.Config `yaml:"audit"` + AuthApp *authapp.Config `yaml:"auth_app"` AuthBasic *authbasic.Config `yaml:"auth_basic"` AuthBearer *authbearer.Config `yaml:"auth_bearer"` AuthMachine *authmachine.Config `yaml:"auth_machine"` diff --git a/ocis-pkg/config/defaultconfig.go b/ocis-pkg/config/defaultconfig.go index 7e284f006..bd15668a7 100644 --- a/ocis-pkg/config/defaultconfig.go +++ b/ocis-pkg/config/defaultconfig.go @@ -6,6 +6,7 @@ import ( appProvider "github.com/owncloud/ocis/v2/services/app-provider/pkg/config/defaults" appRegistry "github.com/owncloud/ocis/v2/services/app-registry/pkg/config/defaults" audit "github.com/owncloud/ocis/v2/services/audit/pkg/config/defaults" + authapp "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/defaults" authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config/defaults" authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config/defaults" authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/config/defaults" @@ -58,6 +59,7 @@ func DefaultConfig() *Config { AppProvider: appProvider.DefaultConfig(), AppRegistry: appRegistry.DefaultConfig(), Audit: audit.DefaultConfig(), + AuthApp: authapp.DefaultConfig(), AuthBasic: authbasic.DefaultConfig(), AuthBearer: authbearer.DefaultConfig(), AuthMachine: authmachine.DefaultConfig(), diff --git a/ocis/pkg/command/services.go b/ocis/pkg/command/services.go index a247e3728..0920dd4e3 100644 --- a/ocis/pkg/command/services.go +++ b/ocis/pkg/command/services.go @@ -13,6 +13,7 @@ import ( appprovider "github.com/owncloud/ocis/v2/services/app-provider/pkg/command" appregistry "github.com/owncloud/ocis/v2/services/app-registry/pkg/command" audit "github.com/owncloud/ocis/v2/services/audit/pkg/command" + authapp "github.com/owncloud/ocis/v2/services/auth-app/pkg/command" authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/command" authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/command" authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/command" @@ -78,6 +79,11 @@ var svccmds = []register.Command{ cfg.Audit.Commons = cfg.Commons }) }, + func(cfg *config.Config) *cli.Command { + return ServiceCommand(cfg, cfg.AuthApp.Service.Name, authapp.GetCommands(cfg.AuthApp), func(_ *config.Config) { + cfg.AuthApp.Commons = cfg.Commons + }) + }, func(cfg *config.Config) *cli.Command { return ServiceCommand(cfg, cfg.AuthBasic.Service.Name, authbasic.GetCommands(cfg.AuthBasic), func(c *config.Config) { cfg.AuthBasic.Commons = cfg.Commons @@ -266,10 +272,10 @@ var svccmds = []register.Command{ } // ServiceCommand is the entry point for the all service commands. -func ServiceCommand(cfg *config.Config, servicename string, subcommands []*cli.Command, f func(*config.Config)) *cli.Command { +func ServiceCommand(cfg *config.Config, serviceName string, subcommands []*cli.Command, f func(*config.Config)) *cli.Command { return &cli.Command{ - Name: servicename, - Usage: helper.SubcommandDescription(servicename), + Name: serviceName, + Usage: helper.SubcommandDescription(serviceName), Category: "services", Before: func(c *cli.Context) error { configlog.Error(parser.ParseConfig(cfg, true)) diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go index 7c8ec280f..f41e2099e 100644 --- a/ocis/pkg/runtime/service/service.go +++ b/ocis/pkg/runtime/service/service.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + authapp "github.com/owncloud/ocis/v2/services/auth-app/pkg/command" "net" "net/http" "net/rpc" @@ -160,6 +161,11 @@ func NewService(options ...Option) (*Service, error) { cfg.AppRegistry.Commons = cfg.Commons return appRegistry.Execute(cfg.AppRegistry) }) + reg(3, opts.Config.AuthApp.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AuthApp.Context = ctx + cfg.AuthApp.Commons = cfg.Commons + return authapp.Execute(cfg.AuthApp) + }) reg(3, opts.Config.AuthBasic.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.AuthBasic.Context = ctx cfg.AuthBasic.Commons = cfg.Commons diff --git a/services/auth-app/Makefile b/services/auth-app/Makefile new file mode 100644 index 000000000..3898eb98c --- /dev/null +++ b/services/auth-app/Makefile @@ -0,0 +1,37 @@ +SHELL := bash +NAME := auth-app + +include ../../.make/recursion.mk + +############ tooling ############ +ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI +include ../../.bingo/Variables.mk +endif + +############ go tooling ############ +include ../../.make/go.mk + +############ release ############ +include ../../.make/release.mk + +############ docs generate ############ +include ../../.make/docs.mk + +.PHONY: docs-generate +docs-generate: config-docs-generate + +############ generate ############ +include ../../.make/generate.mk + +.PHONY: ci-go-generate +ci-go-generate: # CI runs ci-node-generate automatically before this target + +.PHONY: ci-node-generate +ci-node-generate: + +############ licenses ############ +.PHONY: ci-node-check-licenses +ci-node-check-licenses: + +.PHONY: ci-node-save-licenses +ci-node-save-licenses: diff --git a/services/auth-app/README.md b/services/auth-app/README.md new file mode 100644 index 000000000..139ee9225 --- /dev/null +++ b/services/auth-app/README.md @@ -0,0 +1,3 @@ +# Auth-App + +TBD diff --git a/services/auth-app/cmd/auth-app/main.go b/services/auth-app/cmd/auth-app/main.go new file mode 100644 index 000000000..185d6eb9f --- /dev/null +++ b/services/auth-app/cmd/auth-app/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "os" + + "github.com/owncloud/ocis/v2/services/auth-app/pkg/command" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/defaults" +) + +func main() { + if err := command.Execute(defaults.DefaultConfig()); err != nil { + os.Exit(1) + } +} diff --git a/services/auth-app/pkg/command/health.go b/services/auth-app/pkg/command/health.go new file mode 100644 index 000000000..fad375e20 --- /dev/null +++ b/services/auth-app/pkg/command/health.go @@ -0,0 +1,54 @@ +package command + +import ( + "fmt" + "net/http" + + "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/parser" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/logging" + "github.com/urfave/cli/v2" +) + +// Health is the entrypoint for the health command. +func Health(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "health", + Usage: "check health status", + Category: "info", + Before: func(_ *cli.Context) error { + return configlog.ReturnError(parser.ParseConfig(cfg)) + }, + Action: func(_ *cli.Context) error { + logger := logging.Configure(cfg.Service.Name, cfg.Log) + + resp, err := http.Get( + fmt.Sprintf( + "http://%s/healthz", + cfg.Debug.Addr, + ), + ) + + if err != nil { + logger.Fatal(). + Err(err). + Msg("Failed to request health check") + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + logger.Fatal(). + Int("code", resp.StatusCode). + Msg("Health seems to be in bad state") + } + + logger.Debug(). + Int("code", resp.StatusCode). + Msg("Health got a good state") + + return nil + }, + } +} diff --git a/services/auth-app/pkg/command/root.go b/services/auth-app/pkg/command/root.go new file mode 100644 index 000000000..956311824 --- /dev/null +++ b/services/auth-app/pkg/command/root.go @@ -0,0 +1,34 @@ +package command + +import ( + "os" + + "github.com/owncloud/ocis/v2/ocis-pkg/clihelper" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/urfave/cli/v2" +) + +// GetCommands provides all commands for this service +func GetCommands(cfg *config.Config) cli.Commands { + return []*cli.Command{ + // start this service + Server(cfg), + + // interaction with this service + + // infos about this service + Health(cfg), + Version(cfg), + } +} + +// Execute is the entry point for the ocis-auth-app command. +func Execute(cfg *config.Config) error { + app := clihelper.DefaultApp(&cli.App{ + Name: "auth-app", + Usage: "Provide app authentication for oCIS", + Commands: GetCommands(cfg), + }) + + return app.Run(os.Args) +} diff --git a/services/auth-app/pkg/command/server.go b/services/auth-app/pkg/command/server.go new file mode 100644 index 000000000..afb3b54dc --- /dev/null +++ b/services/auth-app/pkg/command/server.go @@ -0,0 +1,104 @@ +package command + +import ( + "context" + "fmt" + "os" + "path" + + "github.com/cs3org/reva/v2/cmd/revad/runtime" + "github.com/gofrs/uuid" + "github.com/oklog/run" + "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/owncloud/ocis/v2/ocis-pkg/sync" + "github.com/owncloud/ocis/v2/ocis-pkg/tracing" + "github.com/owncloud/ocis/v2/ocis-pkg/version" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/parser" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/logging" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/revaconfig" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/server/debug" + "github.com/urfave/cli/v2" +) + +// Server is the entry point for the server command. +func Server(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "server", + Usage: fmt.Sprintf("start the %s service without runtime (unsupervised mode)", cfg.Service.Name), + Category: "server", + Before: func(_ *cli.Context) error { + return configlog.ReturnFatal(parser.ParseConfig(cfg)) + }, + Action: func(_ *cli.Context) error { + logger := logging.Configure(cfg.Service.Name, cfg.Log) + traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name) + if err != nil { + return err + } + gr := run.Group{} + ctx, cancel := defineContext(cfg) + + defer cancel() + + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AuthAppConfigFromStruct(cfg) + reg := registry.GetRegistry() + + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + runtime.WithTraceProvider(traceProvider), + ) + + return nil + }, func(err error) { + logger.Error(). + Str("server", cfg.Service.Name). + Err(err). + Msg("Shutting down server") + + cancel() + os.Exit(1) + }) + + debugServer, err := debug.Server( + debug.Logger(logger), + debug.Context(ctx), + debug.Config(cfg), + ) + if err != nil { + logger.Info().Err(err).Str("server", "debug").Msg("Failed to initialize server") + return err + } + + gr.Add(debugServer.ListenAndServe, func(_ error) { + cancel() + }) + + if !cfg.Supervised { + sync.Trap(&gr, cancel) + } + + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") + } + + return gr.Run() + }, + } +} + +// defineContext sets the context for the service. If there is a context configured it will create a new child from it, +// if not, it will create a root context that can be cancelled. +func defineContext(cfg *config.Config) (context.Context, context.CancelFunc) { + return func() (context.Context, context.CancelFunc) { + if cfg.Context == nil { + return context.WithCancel(context.Background()) + } + return context.WithCancel(cfg.Context) + }() +} diff --git a/services/auth-app/pkg/command/version.go b/services/auth-app/pkg/command/version.go new file mode 100644 index 000000000..b7c644c55 --- /dev/null +++ b/services/auth-app/pkg/command/version.go @@ -0,0 +1,50 @@ +package command + +import ( + "fmt" + "os" + + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/owncloud/ocis/v2/ocis-pkg/version" + + tw "github.com/olekukonko/tablewriter" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/urfave/cli/v2" +) + +// Version prints the service versions of all running instances. +func Version(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "version", + Usage: "print the version of this binary and the running service instances", + Category: "info", + Action: func(c *cli.Context) error { + fmt.Println("Version: " + version.GetString()) + fmt.Printf("Compiled: %s\n", version.Compiled()) + fmt.Println("") + + reg := registry.GetRegistry() + services, err := reg.GetService(cfg.GRPC.Namespace + "." + cfg.Service.Name) + if err != nil { + fmt.Println(fmt.Errorf("could not get %s services from the registry: %v", cfg.Service.Name, err)) + return err + } + + if len(services) == 0 { + fmt.Println("No running " + cfg.Service.Name + " service found.") + return nil + } + + table := tw.NewWriter(os.Stdout) + table.SetHeader([]string{"Version", "Address", "Id"}) + table.SetAutoFormatHeaders(false) + for _, s := range services { + for _, n := range s.Nodes { + table.Append([]string{s.Version, n.Address, n.Id}) + } + } + table.Render() + return nil + }, + } +} diff --git a/services/auth-app/pkg/config/config.go b/services/auth-app/pkg/config/config.go new file mode 100644 index 000000000..d3feceb46 --- /dev/null +++ b/services/auth-app/pkg/config/config.go @@ -0,0 +1,55 @@ +package config + +import ( + "context" + + "github.com/owncloud/ocis/v2/ocis-pkg/shared" +) + +// Config defines the root config structure +type Config struct { + Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service + Service Service `yaml:"-"` + Tracing *Tracing `yaml:"tracing"` + Log *Log `yaml:"log"` + Debug Debug `yaml:"debug"` + + GRPC GRPCConfig `yaml:"grpc"` + + TokenManager *TokenManager `yaml:"token_manager"` + Reva *shared.Reva `yaml:"reva"` + + SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"AUTH_APP_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the encoding of the user's group memberships in the reva access token. This reduces the token size, especially when users are members of a large number of groups." introductionVersion:"pre5.0"` + + Supervised bool `yaml:"-"` + Context context.Context `yaml:"-"` +} + +// Log defines the loging configuration +type Log struct { + Level string `yaml:"level" env:"OCIS_LOG_LEVEL;AUTH_APP_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"pre5.0"` + Pretty bool `yaml:"pretty" env:"OCIS_LOG_PRETTY;AUTH_APP_LOG_PRETTY" desc:"Activates pretty log output." introductionVersion:"pre5.0"` + Color bool `yaml:"color" env:"OCIS_LOG_COLOR;AUTH_APP_LOG_COLOR" desc:"Activates colorized log output." introductionVersion:"pre5.0"` + File string `yaml:"file" env:"OCIS_LOG_FILE;AUTH_APP_LOG_FILE" desc:"The path to the log file. Activates logging to this file if set." introductionVersion:"pre5.0"` +} + +// Service defines the service configuration +type Service struct { + Name string `yaml:"-"` +} + +// Debug defines the debug configuration +type Debug struct { + Addr string `yaml:"addr" env:"AUTH_APP_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed." introductionVersion:"pre5.0"` + Token string `yaml:"token" env:"AUTH_APP_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint." introductionVersion:"pre5.0"` + Pprof bool `yaml:"pprof" env:"AUTH_APP_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling." introductionVersion:"pre5.0"` + Zpages bool `yaml:"zpages" env:"AUTH_APP_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing traces in-memory." introductionVersion:"pre5.0"` +} + +// GRPCConfig defines the GRPC configuration +type GRPCConfig struct { + Addr string `yaml:"addr" env:"AUTH_APP_GRPC_ADDR" desc:"The bind address of the GRPC service." introductionVersion:"pre5.0"` + TLS *shared.GRPCServiceTLS `yaml:"tls"` + Namespace string `yaml:"-"` + Protocol string `yaml:"protocol" env:"AUTH_APP_GRPC_PROTOCOL" desc:"The transport protocol of the GRPC service." introductionVersion:"pre5.0"` +} diff --git a/services/auth-app/pkg/config/defaults/defaultconfig.go b/services/auth-app/pkg/config/defaults/defaultconfig.go new file mode 100644 index 000000000..efc4028f3 --- /dev/null +++ b/services/auth-app/pkg/config/defaults/defaultconfig.go @@ -0,0 +1,83 @@ +package defaults + +import ( + "github.com/owncloud/ocis/v2/ocis-pkg/shared" + "github.com/owncloud/ocis/v2/ocis-pkg/structs" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" +) + +// FullDefaultConfig returns a fully initialized default configuration +func FullDefaultConfig() *config.Config { + cfg := DefaultConfig() + EnsureDefaults(cfg) + Sanitize(cfg) + return cfg +} + +// DefaultConfig returns a basic default configuration +func DefaultConfig() *config.Config { + return &config.Config{ + Debug: config.Debug{ + Addr: "127.0.0.1:9197", + Token: "", + Pprof: false, + Zpages: false, + }, + GRPC: config.GRPCConfig{ + Addr: "127.0.0.1:9195", + Namespace: "com.owncloud.api", + Protocol: "tcp", + }, + Service: config.Service{ + Name: "auth-app", + }, + Reva: shared.DefaultRevaConfig(), + } +} + +// EnsureDefaults adds default values to the configuration if they are not set yet +func EnsureDefaults(cfg *config.Config) { + // provide with defaults for shared logging, since we need a valid destination address for "envdecode". + if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil { + cfg.Log = &config.Log{ + Level: cfg.Commons.Log.Level, + Pretty: cfg.Commons.Log.Pretty, + Color: cfg.Commons.Log.Color, + File: cfg.Commons.Log.File, + } + } else if cfg.Log == nil { + cfg.Log = &config.Log{} + } + // provide with defaults for shared tracing, since we need a valid destination address for "envdecode". + if cfg.Tracing == nil && cfg.Commons != nil && cfg.Commons.Tracing != nil { + cfg.Tracing = &config.Tracing{ + Enabled: cfg.Commons.Tracing.Enabled, + Type: cfg.Commons.Tracing.Type, + Endpoint: cfg.Commons.Tracing.Endpoint, + Collector: cfg.Commons.Tracing.Collector, + } + } else if cfg.Tracing == nil { + cfg.Tracing = &config.Tracing{} + } + + if cfg.Reva == nil && cfg.Commons != nil { + cfg.Reva = structs.CopyOrZeroValue(cfg.Commons.Reva) + } + + if cfg.TokenManager == nil && cfg.Commons != nil && cfg.Commons.TokenManager != nil { + cfg.TokenManager = &config.TokenManager{ + JWTSecret: cfg.Commons.TokenManager.JWTSecret, + } + } else if cfg.TokenManager == nil { + cfg.TokenManager = &config.TokenManager{} + } + + if cfg.GRPC.TLS == nil && cfg.Commons != nil { + cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS) + } +} + +// Sanitize sanitized the configuration +func Sanitize(_ *config.Config) { + // nothing to sanitize here atm +} diff --git a/services/auth-app/pkg/config/parser/parse.go b/services/auth-app/pkg/config/parser/parse.go new file mode 100644 index 000000000..d9c71d927 --- /dev/null +++ b/services/auth-app/pkg/config/parser/parse.go @@ -0,0 +1,42 @@ +package parser + +import ( + "errors" + + ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config" + "github.com/owncloud/ocis/v2/ocis-pkg/shared" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config/defaults" + + "github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode" +) + +// ParseConfig loads configuration from known paths. +func ParseConfig(cfg *config.Config) error { + err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg) + if err != nil { + return err + } + + defaults.EnsureDefaults(cfg) + + // load all env variables relevant to the config in the current context. + if err := envdecode.Decode(cfg); err != nil { + // no environment variable set for this config is an expected "error" + if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) { + return err + } + } + + defaults.Sanitize(cfg) + + return Validate(cfg) +} + +func Validate(cfg *config.Config) error { + if cfg.TokenManager.JWTSecret == "" { + return shared.MissingJWTTokenError(cfg.Service.Name) + } + + return nil +} diff --git a/services/auth-app/pkg/config/reva.go b/services/auth-app/pkg/config/reva.go new file mode 100644 index 000000000..92aa14085 --- /dev/null +++ b/services/auth-app/pkg/config/reva.go @@ -0,0 +1,6 @@ +package config + +// TokenManager is the config for using the reva token manager +type TokenManager struct { + JWTSecret string `yaml:"jwt_secret" env:"OCIS_JWT_SECRET;AUTH_APP_JWT_SECRET" desc:"The secret to mint and validate jwt tokens." introductionVersion:"pre5.0"` +} diff --git a/services/auth-app/pkg/config/tracing.go b/services/auth-app/pkg/config/tracing.go new file mode 100644 index 000000000..b33f26341 --- /dev/null +++ b/services/auth-app/pkg/config/tracing.go @@ -0,0 +1,21 @@ +package config + +import "github.com/owncloud/ocis/v2/ocis-pkg/tracing" + +// Tracing defines the tracing configuration. +type Tracing struct { + Enabled bool `yaml:"enabled" env:"OCIS_TRACING_ENABLED;AUTH_APP_TRACING_ENABLED" desc:"Activates tracing." introductionVersion:"pre5.0"` + Type string `yaml:"type" env:"OCIS_TRACING_TYPE;AUTH_APP_TRACING_TYPE" desc:"The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now." introductionVersion:"pre5.0"` + Endpoint string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;AUTH_APP_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent." introductionVersion:"pre5.0"` + Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;AUTH_APP_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset." introductionVersion:"pre5.0"` +} + +// Convert Tracing to the tracing package's Config struct. +func (t Tracing) Convert() tracing.Config { + return tracing.Config{ + Enabled: t.Enabled, + Type: t.Type, + Endpoint: t.Endpoint, + Collector: t.Collector, + } +} diff --git a/services/auth-app/pkg/logging/logging.go b/services/auth-app/pkg/logging/logging.go new file mode 100644 index 000000000..b01a17932 --- /dev/null +++ b/services/auth-app/pkg/logging/logging.go @@ -0,0 +1,17 @@ +package logging + +import ( + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" +) + +// Configure initializes a service-specific logger instance. +func Configure(name string, cfg *config.Log) log.Logger { + return log.NewLogger( + log.Name(name), + log.Level(cfg.Level), + log.Pretty(cfg.Pretty), + log.Color(cfg.Color), + log.File(cfg.File), + ) +} diff --git a/services/auth-app/pkg/revaconfig/config.go b/services/auth-app/pkg/revaconfig/config.go new file mode 100644 index 000000000..1a015fcd1 --- /dev/null +++ b/services/auth-app/pkg/revaconfig/config.go @@ -0,0 +1,43 @@ +package revaconfig + +import ( + "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{} { + rcfg := map[string]interface{}{ + "shared": map[string]interface{}{ + "jwt_secret": cfg.TokenManager.JWTSecret, + "gatewaysvc": cfg.Reva.Address, + "skip_user_groups_in_token": cfg.SkipUserGroupsInToken, + "grpc_client_options": cfg.Reva.GetGRPCClientConfig(), + }, + "grpc": map[string]interface{}{ + "network": cfg.GRPC.Protocol, + "address": cfg.GRPC.Addr, + "tls_settings": map[string]interface{}{ + "enabled": cfg.GRPC.TLS.Enabled, + "certificate": cfg.GRPC.TLS.Cert, + "key": cfg.GRPC.TLS.Key, + }, + "services": map[string]interface{}{ + "authprovider": map[string]interface{}{ + "auth_manager": "appauth", + "auth_managers": map[string]interface{}{ + "appauth": map[string]interface{}{ + "gateway_addr": cfg.Reva.Address, + }, + }, + }, + }, + "interceptors": map[string]interface{}{ + "prometheus": map[string]interface{}{ + "namespace": "ocis", + "subsystem": "auth_app", + }, + }, + }, + } + return rcfg +} diff --git a/services/auth-app/pkg/server/debug/option.go b/services/auth-app/pkg/server/debug/option.go new file mode 100644 index 000000000..81f515267 --- /dev/null +++ b/services/auth-app/pkg/server/debug/option.go @@ -0,0 +1,50 @@ +package debug + +import ( + "context" + + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Logger log.Logger + Context context.Context + Config *config.Config +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Context provides a function to set the context option. +func Context(val context.Context) Option { + return func(o *Options) { + o.Context = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} diff --git a/services/auth-app/pkg/server/debug/server.go b/services/auth-app/pkg/server/debug/server.go new file mode 100644 index 000000000..b87b7e52f --- /dev/null +++ b/services/auth-app/pkg/server/debug/server.go @@ -0,0 +1,63 @@ +package debug + +import ( + "io" + "net/http" + + "github.com/owncloud/ocis/v2/ocis-pkg/service/debug" + "github.com/owncloud/ocis/v2/ocis-pkg/version" + "github.com/owncloud/ocis/v2/services/auth-app/pkg/config" +) + +// Server initializes the debug service and server. +func Server(opts ...Option) (*http.Server, error) { + options := newOptions(opts...) + + return debug.NewService( + debug.Logger(options.Logger), + debug.Name(options.Config.Service.Name), + debug.Version(version.GetString()), + debug.Address(options.Config.Debug.Addr), + debug.Token(options.Config.Debug.Token), + debug.Pprof(options.Config.Debug.Pprof), + debug.Zpages(options.Config.Debug.Zpages), + debug.Health(health(options.Config)), + debug.Ready(ready(options.Config)), + //debug.CorsAllowedOrigins(options.Config.HTTP.CORS.AllowedOrigins), + //debug.CorsAllowedMethods(options.Config.HTTP.CORS.AllowedMethods), + //debug.CorsAllowedHeaders(options.Config.HTTP.CORS.AllowedHeaders), + //debug.CorsAllowCredentials(options.Config.HTTP.CORS.AllowCredentials), + ), nil +} + +// health implements the health check. +func health(_ *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + // TODO: check if services are up and running + + _, err := io.WriteString(w, http.StatusText(http.StatusOK)) + // io.WriteString should not fail but if it does, we want to know. + if err != nil { + panic(err) + } + } +} + +// ready implements the ready check. +func ready(_ *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + // TODO: check if services are up and running + + _, err := io.WriteString(w, http.StatusText(http.StatusOK)) + // io.WriteString should not fail but if it does, we want to know. + if err != nil { + panic(err) + } + } +}