Skip to content

Commit

Permalink
Merge pull request #9755 from kobergj/AppTokenApi
Browse files Browse the repository at this point in the history
App Token API
  • Loading branch information
kobergj authored Aug 9, 2024
2 parents 4520dfa + 5413b8e commit a47cd02
Show file tree
Hide file tree
Showing 10 changed files with 705 additions and 3 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/auth-app-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add an API to auth-app service

Adds an API to create, list and delete app tokens. Includes an impersonification feature for migration scenarios.

https://github.com/owncloud/ocis/pull/9755
15 changes: 14 additions & 1 deletion services/auth-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,23 @@ PROXY_ENABLE_APP_AUTH=true # mandatory, allow app authentication. In case o

## App Tokens

App Tokens are used to authenticate 3rd party access via https like when using curl (apps) to access an API endpoint. These apps need to authenticate themselves as no logged in user authenticates the request. To be able to use an app token, one must first create a token via the cli. Replace the `user-name` with an existing user. For the `token-expiration`, you can use any time abbreviation from the following list: `h, m, s`. Examples: `72h` or `1h` or `1m` or `1s.` Default is `72h`.
App Tokens are used to authenticate 3rd party access via https like when using curl (apps) to access an API endpoint. These apps need to authenticate themselves as no logged in user authenticates the request. To be able to use an app token, one must first create a token. There are different options of creating a token.

### Via CLI (dev only)

Replace the `user-name` with an existing user. For the `token-expiration`, you can use any time abbreviation from the following list: `h, m, s`. Examples: `72h` or `1h` or `1m` or `1s.` Default is `72h`.

```bash
ocis auth-app create --user-name={user-name} --expiration={token-expiration}
```

Once generated, these tokens can be used to authenticate requests to ocis. They are passed as part of the request as `Basic Auth` header.

### Via API

The `auth-app` service provides an API to create (POST), list (GET) and delete (DELETE) tokens at `/auth-app/tokens`.

### Via Impersonation API

When setting the environment variable `AUTH_APP_ENABLE_IMPERSONATION` to `true`, admins will be able to use the `/auth-app/tokens` endpoint to create tokens for other users. This is crucial for migration scenarios,
but should not be used on a productive system.
49 changes: 49 additions & 0 deletions services/auth-app/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ import (
"path"

"github.com/cs3org/reva/v2/cmd/revad/runtime"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"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"
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
"github.com/owncloud/ocis/v2/ocis-pkg/sync"
"github.com/owncloud/ocis/v2/ocis-pkg/tracing"
"github.com/owncloud/ocis/v2/ocis-pkg/version"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"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/owncloud/ocis/v2/services/auth-app/pkg/server/http"
"github.com/urfave/cli/v2"
)

Expand All @@ -32,6 +36,10 @@ func Server(cfg *config.Config) *cli.Command {
return configlog.ReturnFatal(parser.ParseConfig(cfg))
},
Action: func(c *cli.Context) error {
if cfg.AllowImpersonation {
fmt.Println("WARNING: Impersonation is enabled. Admins can impersonate all users.")
}

logger := logging.Configure(cfg.Service.Name, cfg.Log)
traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
if err != nil {
Expand Down Expand Up @@ -86,6 +94,47 @@ func Server(cfg *config.Config) *cli.Command {
logger.Fatal().Err(err).Msg("failed to register the grpc service")
}

tm, err := pool.StringToTLSMode(cfg.GRPCClientTLS.Mode)
if err != nil {
return err
}
gatewaySelector, err := pool.GatewaySelector(
cfg.Reva.Address,
append(
cfg.Reva.GetRevaOptions(),
pool.WithTLSCACert(cfg.GRPCClientTLS.CACert),
pool.WithTLSMode(tm),
pool.WithRegistry(registry.GetRegistry()),
pool.WithTracerProvider(traceProvider),
)...)
if err != nil {
return err
}

grpcClient, err := ogrpc.NewClient(
append(ogrpc.GetClientOptions(cfg.GRPCClientTLS), ogrpc.WithTraceProvider(traceProvider))...,
)
if err != nil {
return err
}

rClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpcClient)
server, err := http.Server(
http.Logger(logger),
http.Context(ctx),
http.Config(cfg),
http.GatewaySelector(gatewaySelector),
http.RoleClient(rClient),
http.TracerProvider(traceProvider),
)
if err != nil {
logger.Fatal().Err(err).Msg("failed to initialize http server")
}

gr.Add(server.Run, func(err error) {
logger.Error().Err(err).Str("server", "http").Msg("shutting down server")
})

return gr.Run()
},
}
Expand Down
22 changes: 22 additions & 0 deletions services/auth-app/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ type Config struct {
Debug Debug `yaml:"debug"`

GRPC GRPCConfig `yaml:"grpc"`
HTTP HTTP `yaml:"http"`

GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`

TokenManager *TokenManager `yaml:"token_manager"`
Reva *shared.Reva `yaml:"reva"`
Expand All @@ -23,6 +26,8 @@ type Config struct {

MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;AUTH_APP_MACHINE_AUTH_API_KEY" desc:"The machine auth API key used to validate internal requests necessary to access resources from other services." introductionVersion:"%%NEXT%%"`

AllowImpersonation bool `yaml:"allow_impersonation" env:"AUTH_APP_ENABLE_IMPERSONATION" desc:"Allows admins to create app tokens for other users. Used for migration. Do NOT use in productive deployments." introductionVersion:"%%NEXT%%"`

Supervised bool `yaml:"-"`
Context context.Context `yaml:"-"`
}
Expand Down Expand Up @@ -55,3 +60,20 @@ type GRPCConfig struct {
Namespace string `yaml:"-"`
Protocol string `yaml:"protocol" env:"AUTH_APP_GRPC_PROTOCOL" desc:"The transport protocol of the GRPC service." introductionVersion:"%%NEXT%%"`
}

// HTTP defines the available http configuration.
type HTTP struct {
Addr string `yaml:"addr" env:"AUTH_APP_HTTP_ADDR" desc:"The bind address of the HTTP service." introductionVersion:"pre5.0"`
Namespace string `yaml:"-"`
Root string `yaml:"root" env:"AUTH_APP_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service." introductionVersion:"pre5.0"`
CORS CORS `yaml:"cors"`
TLS shared.HTTPServiceTLS `yaml:"tls"`
}

// CORS defines the available cors configuration.
type CORS struct {
AllowedOrigins []string `yaml:"allow_origins" env:"OCIS_CORS_ALLOW_ORIGINS;AUTH_APP_CORS_ALLOW_ORIGINS" desc:"A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details." introductionVersion:"pre5.0"`
AllowedMethods []string `yaml:"allow_methods" env:"OCIS_CORS_ALLOW_METHODS;AUTH_APP_CORS_ALLOW_METHODS" desc:"A list of allowed CORS methods. See following chapter for more details: *Access-Control-Request-Method* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Method. See the Environment Variable Types description for more details." introductionVersion:"pre5.0"`
AllowedHeaders []string `yaml:"allow_headers" env:"OCIS_CORS_ALLOW_HEADERS;AUTH_APP_CORS_ALLOW_HEADERS" desc:"A list of allowed CORS headers. See following chapter for more details: *Access-Control-Request-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers. See the Environment Variable Types description for more details." introductionVersion:"pre5.0"`
AllowCredentials bool `yaml:"allow_credentials" env:"OCIS_CORS_ALLOW_CREDENTIALS;AUTH_APP_CORS_ALLOW_CREDENTIALS" desc:"Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials." introductionVersion:"pre5.0"`
}
28 changes: 26 additions & 2 deletions services/auth-app/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package defaults

import (
"strings"

"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"
Expand Down Expand Up @@ -28,6 +30,17 @@ func DefaultConfig() *config.Config {
Namespace: "com.owncloud.api",
Protocol: "tcp",
},
HTTP: config.HTTP{
Addr: "127.0.0.1:9247",
Namespace: "com.owncloud.web",
Root: "/",
CORS: config.CORS{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "DELETE"},
AllowedHeaders: []string{"Authorization", "Origin", "Content-Type", "Accept", "X-Requested-With", "X-Request-Id", "Ocs-Apirequest"},
AllowCredentials: true,
},
},
Service: config.Service{
Name: "auth-app",
},
Expand Down Expand Up @@ -60,6 +73,10 @@ func EnsureDefaults(cfg *config.Config) {
cfg.Tracing = &config.Tracing{}
}

if cfg.GRPCClientTLS == nil && cfg.Commons != nil {
cfg.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS)
}

if cfg.Reva == nil && cfg.Commons != nil {
cfg.Reva = structs.CopyOrZeroValue(cfg.Commons.Reva)
}
Expand All @@ -79,9 +96,16 @@ func EnsureDefaults(cfg *config.Config) {
if cfg.GRPC.TLS == nil && cfg.Commons != nil {
cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS)
}

if cfg.Commons != nil {
cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
}
}

// Sanitize sanitized the configuration
func Sanitize(_ *config.Config) {
// nothing to sanitize here atm
func Sanitize(cfg *config.Config) {
// sanitize config
if cfg.HTTP.Root != "/" {
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
}
}
95 changes: 95 additions & 0 deletions services/auth-app/pkg/server/http/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package http

import (
"context"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/auth-app/pkg/config"
"github.com/urfave/cli/v2"
"go.opentelemetry.io/otel/trace"
)

// 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
Flags []cli.Flag
Namespace string
GatewaySelector pool.Selectable[gateway.GatewayAPIClient]
RoleClient settingssvc.RoleService
TracerProvider trace.TracerProvider
}

// 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
}
}

// Flags provides a function to set the flags option.
func Flags(val []cli.Flag) Option {
return func(o *Options) {
o.Flags = append(o.Flags, val...)
}
}

// Namespace provides a function to set the Namespace option.
func Namespace(val string) Option {
return func(o *Options) {
o.Namespace = val
}
}

// GatewaySelector provides a function to configure the gateway client selector
func GatewaySelector(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Option {
return func(o *Options) {
o.GatewaySelector = gatewaySelector
}
}

// RoleClient adds a grpc client for the role service
func RoleClient(rs settingssvc.RoleService) Option {
return func(o *Options) {
o.RoleClient = rs
}
}

// TracerProvider provides a function to set the TracerProvider option
func TracerProvider(val trace.TracerProvider) Option {
return func(o *Options) {
o.TracerProvider = val
}
}
Loading

0 comments on commit a47cd02

Please sign in to comment.