mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 04:09:40 -06:00
Add 'settings/' from commit '230545a4a75b0611988fbcea51336a6c316d6a3d'
git-subtree-dir: settings git-subtree-mainline:c26f7b390agit-subtree-split:230545a4a7
This commit is contained in:
66
settings/pkg/assets/assets.go
Normal file
66
settings/pkg/assets/assets.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
|
||||
// Fake the import to make the dep tree happy.
|
||||
_ "golang.org/x/net/context"
|
||||
|
||||
// Fake the import to make the dep tree happy.
|
||||
_ "golang.org/x/net/webdav"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/UnnoTed/fileb0x embed.yml
|
||||
|
||||
// assets gets initialized by New and provides the handler.
|
||||
type assets struct {
|
||||
logger log.Logger
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// Open just implements the HTTP filesystem interface.
|
||||
func (a assets) Open(original string) (http.File, error) {
|
||||
if a.config.Asset.Path != "" {
|
||||
if stat, err := os.Stat(a.config.Asset.Path); err == nil && stat.IsDir() {
|
||||
custom := filepath.Join(
|
||||
a.config.Asset.Path,
|
||||
original,
|
||||
)
|
||||
|
||||
if _, err := os.Stat(custom); !os.IsNotExist(err) {
|
||||
f, err := os.Open(custom)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
} else {
|
||||
a.logger.Warn().
|
||||
Str("path", a.config.Asset.Path).
|
||||
Msg("Assets directory doesn't exist")
|
||||
}
|
||||
}
|
||||
|
||||
return FS.OpenFile(
|
||||
CTX,
|
||||
original,
|
||||
os.O_RDONLY,
|
||||
0644,
|
||||
)
|
||||
}
|
||||
|
||||
// New returns a new http filesystem to serve assets.
|
||||
func New(opts ...Option) http.FileSystem {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return assets{
|
||||
config: options.Config,
|
||||
}
|
||||
}
|
||||
202
settings/pkg/assets/embed.go
Normal file
202
settings/pkg/assets/embed.go
Normal file
File diff suppressed because one or more lines are too long
17
settings/pkg/assets/embed.yml
Normal file
17
settings/pkg/assets/embed.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
pkg: "assets"
|
||||
dest: "."
|
||||
output: "embed.go"
|
||||
fmt: true
|
||||
noprefix: true
|
||||
|
||||
compression:
|
||||
compress: true
|
||||
|
||||
custom:
|
||||
- files:
|
||||
- "../../assets/"
|
||||
base: "../../assets/"
|
||||
prefix: ""
|
||||
|
||||
...
|
||||
40
settings/pkg/assets/option.go
Normal file
40
settings/pkg/assets/option.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
49
settings/pkg/command/health.go
Normal file
49
settings/pkg/command/health.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/flagset"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
func Health(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "health",
|
||||
Usage: "Check health status",
|
||||
Flags: flagset.HealthWithConfig(cfg),
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
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 != 200 {
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
108
settings/pkg/command/root.go
Normal file
108
settings/pkg/command/root.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/flagset"
|
||||
"github.com/owncloud/ocis-settings/pkg/version"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Execute is the entry point for the ocis-settings command.
|
||||
func Execute() error {
|
||||
cfg := config.New()
|
||||
|
||||
app := &cli.App{
|
||||
Name: "ocis-settings",
|
||||
Version: version.String,
|
||||
Usage: "Provide settings and permissions for oCIS",
|
||||
Compiled: version.Compiled(),
|
||||
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "ownCloud GmbH",
|
||||
Email: "support@owncloud.com",
|
||||
},
|
||||
},
|
||||
|
||||
Flags: flagset.RootWithConfig(cfg),
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
return ParseConfig(c, cfg)
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
Server(cfg),
|
||||
Health(cfg),
|
||||
},
|
||||
}
|
||||
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "help,h",
|
||||
Usage: "Show the help",
|
||||
}
|
||||
|
||||
cli.VersionFlag = &cli.BoolFlag{
|
||||
Name: "version,v",
|
||||
Usage: "Print the version",
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
// NewLogger initializes a service-specific logger instance.
|
||||
func NewLogger(cfg *config.Config) log.Logger {
|
||||
return log.NewLogger(
|
||||
log.Name("settings"),
|
||||
log.Level(cfg.Log.Level),
|
||||
log.Pretty(cfg.Log.Pretty),
|
||||
log.Color(cfg.Log.Color),
|
||||
)
|
||||
}
|
||||
|
||||
// ParseConfig loads settings configuration from Viper known paths.
|
||||
func ParseConfig(c *cli.Context, cfg *config.Config) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.SetEnvPrefix("SETTINGS")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if c.IsSet("config-file") {
|
||||
viper.SetConfigFile(c.String("config-file"))
|
||||
} else {
|
||||
viper.SetConfigName("settings")
|
||||
|
||||
viper.AddConfigPath("/etc/ocis")
|
||||
viper.AddConfigPath("$HOME/.ocis")
|
||||
viper.AddConfigPath("./config")
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
switch err.(type) {
|
||||
case viper.ConfigFileNotFoundError:
|
||||
logger.Info().
|
||||
Msg("Continue without config")
|
||||
case viper.UnsupportedConfigError:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Unsupported config type")
|
||||
default:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to read config")
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to parse config")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
224
settings/pkg/command/server.go
Normal file
224
settings/pkg/command/server.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/jaeger"
|
||||
"contrib.go.opencensus.io/exporter/ocagent"
|
||||
"contrib.go.opencensus.io/exporter/zipkin"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/oklog/run"
|
||||
openzipkin "github.com/openzipkin/zipkin-go"
|
||||
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/flagset"
|
||||
"github.com/owncloud/ocis-settings/pkg/server/debug"
|
||||
"github.com/owncloud/ocis-settings/pkg/server/grpc"
|
||||
"github.com/owncloud/ocis-settings/pkg/server/http"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// Server is the entrypoint for the server command.
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: "Start integrated server",
|
||||
Flags: flagset.ServerWithConfig(cfg),
|
||||
Before: func(ctx *cli.Context) error {
|
||||
if cfg.HTTP.Root != "/" {
|
||||
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
|
||||
}
|
||||
|
||||
// When running on single binary mode the before hook from the root command won't get called. We manually
|
||||
// call this before hook from ocis command, so the configuration can be loaded.
|
||||
return ParseConfig(ctx, cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
if cfg.Tracing.Enabled {
|
||||
switch t := cfg.Tracing.Type; t {
|
||||
case "agent":
|
||||
exporter, err := ocagent.NewExporter(
|
||||
ocagent.WithReconnectionPeriod(5*time.Second),
|
||||
ocagent.WithAddress(cfg.Tracing.Endpoint),
|
||||
ocagent.WithServiceName(cfg.Tracing.Service),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create agent tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
view.RegisterExporter(exporter)
|
||||
|
||||
case "jaeger":
|
||||
exporter, err := jaeger.NewExporter(
|
||||
jaeger.Options{
|
||||
AgentEndpoint: cfg.Tracing.Endpoint,
|
||||
CollectorEndpoint: cfg.Tracing.Collector,
|
||||
ServiceName: cfg.Tracing.Service,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create jaeger tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
|
||||
case "zipkin":
|
||||
endpoint, err := openzipkin.NewEndpoint(
|
||||
cfg.Tracing.Service,
|
||||
cfg.Tracing.Endpoint,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create zipkin tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
exporter := zipkin.NewExporter(
|
||||
zipkinhttp.NewReporter(
|
||||
cfg.Tracing.Collector,
|
||||
),
|
||||
endpoint,
|
||||
)
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
|
||||
default:
|
||||
logger.Warn().
|
||||
Str("type", t).
|
||||
Msg("Unknown tracing backend")
|
||||
}
|
||||
|
||||
trace.ApplyConfig(
|
||||
trace.Config{
|
||||
DefaultSampler: trace.AlwaysSample(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
logger.Debug().
|
||||
Msg("Tracing is not enabled")
|
||||
}
|
||||
|
||||
var (
|
||||
gr = run.Group{}
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
)
|
||||
|
||||
defer cancel()
|
||||
|
||||
{
|
||||
server := http.Server(
|
||||
http.Name("settings"),
|
||||
http.Logger(logger),
|
||||
http.Context(ctx),
|
||||
http.Config(cfg),
|
||||
http.Flags(flagset.RootWithConfig(cfg)),
|
||||
http.Flags(flagset.ServerWithConfig(cfg)),
|
||||
)
|
||||
|
||||
gr.Add(server.Run, func(_ error) {
|
||||
logger.Info().
|
||||
Str("server", "http").
|
||||
Msg("Shutting down server")
|
||||
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
server := grpc.Server(
|
||||
grpc.Name("settings"),
|
||||
grpc.Logger(logger),
|
||||
grpc.Context(ctx),
|
||||
grpc.Config(cfg),
|
||||
)
|
||||
|
||||
gr.Add(server.Run, func(_ error) {
|
||||
logger.Info().
|
||||
Str("server", "grpc").
|
||||
Msg("Shutting down server")
|
||||
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
server, err := debug.Server(
|
||||
debug.Logger(logger),
|
||||
debug.Context(ctx),
|
||||
debug.Config(cfg),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("server", "debug").
|
||||
Msg("Failed to initialize server")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Add(server.ListenAndServe, func(_ error) {
|
||||
ctx, timeout := context.WithTimeout(ctx, 5*time.Second)
|
||||
|
||||
defer timeout()
|
||||
defer cancel()
|
||||
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("server", "debug").
|
||||
Msg("Failed to shutdown server")
|
||||
} else {
|
||||
logger.Info().
|
||||
Str("server", "debug").
|
||||
Msg("Shutting down server")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
||||
gr.Add(func() error {
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
|
||||
<-stop
|
||||
|
||||
return nil
|
||||
}, func(err error) {
|
||||
close(stop)
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
return gr.Run()
|
||||
},
|
||||
}
|
||||
}
|
||||
71
settings/pkg/config/config.go
Normal file
71
settings/pkg/config/config.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package config
|
||||
|
||||
// Log defines the available logging configuration.
|
||||
type Log struct {
|
||||
Level string
|
||||
Pretty bool
|
||||
Color bool
|
||||
}
|
||||
|
||||
// Debug defines the available debug configuration.
|
||||
type Debug struct {
|
||||
Addr string
|
||||
Token string
|
||||
Pprof bool
|
||||
Zpages bool
|
||||
}
|
||||
|
||||
// HTTP defines the available http configuration.
|
||||
type HTTP struct {
|
||||
Addr string
|
||||
Namespace string
|
||||
Root string
|
||||
}
|
||||
|
||||
// GRPC defines the available grpc configuration.
|
||||
type GRPC struct {
|
||||
Addr string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// Tracing defines the available tracing configuration.
|
||||
type Tracing struct {
|
||||
Enabled bool
|
||||
Type string
|
||||
Endpoint string
|
||||
Collector string
|
||||
Service string
|
||||
}
|
||||
|
||||
// Asset undocumented
|
||||
type Asset struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Storage defines the available storage configuration.
|
||||
type Storage struct {
|
||||
DataPath string
|
||||
}
|
||||
|
||||
// TokenManager is the config for using the reva token manager
|
||||
type TokenManager struct {
|
||||
JWTSecret string
|
||||
}
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
File string
|
||||
Storage Storage
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
GRPC GRPC
|
||||
Tracing Tracing
|
||||
Asset Asset
|
||||
TokenManager TokenManager
|
||||
}
|
||||
|
||||
// New initializes a new configuration with or without defaults.
|
||||
func New() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
175
settings/pkg/flagset/flagset.go
Normal file
175
settings/pkg/flagset/flagset.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package flagset
|
||||
|
||||
import (
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
)
|
||||
|
||||
// RootWithConfig applies cfg to the root flagset
|
||||
func RootWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "config-file",
|
||||
Value: "",
|
||||
Usage: "Path to config file",
|
||||
EnvVars: []string{"SETTINGS_CONFIG_FILE"},
|
||||
Destination: &cfg.File,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "log-level",
|
||||
Value: "info",
|
||||
Usage: "Set logging level",
|
||||
EnvVars: []string{"SETTINGS_LOG_LEVEL"},
|
||||
Destination: &cfg.Log.Level,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "log-pretty",
|
||||
Value: true,
|
||||
Usage: "Enable pretty logging",
|
||||
EnvVars: []string{"SETTINGS_LOG_PRETTY"},
|
||||
Destination: &cfg.Log.Pretty,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "log-color",
|
||||
Value: true,
|
||||
Usage: "Enable colored logging",
|
||||
EnvVars: []string{"SETTINGS_LOG_COLOR"},
|
||||
Destination: &cfg.Log.Color,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// HealthWithConfig applies cfg to the root flagset
|
||||
func HealthWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "debug-addr",
|
||||
Value: "0.0.0.0:9194",
|
||||
Usage: "Address to debug endpoint",
|
||||
EnvVars: []string{"SETTINGS_DEBUG_ADDR"},
|
||||
Destination: &cfg.Debug.Addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ServerWithConfig applies cfg to the root flagset
|
||||
func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "tracing-enabled",
|
||||
Usage: "Enable sending traces",
|
||||
EnvVars: []string{"SETTINGS_TRACING_ENABLED"},
|
||||
Destination: &cfg.Tracing.Enabled,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-type",
|
||||
Value: "jaeger",
|
||||
Usage: "Tracing backend type",
|
||||
EnvVars: []string{"SETTINGS_TRACING_TYPE"},
|
||||
Destination: &cfg.Tracing.Type,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-endpoint",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the agent",
|
||||
EnvVars: []string{"SETTINGS_TRACING_ENDPOINT"},
|
||||
Destination: &cfg.Tracing.Endpoint,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-collector",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the collector",
|
||||
EnvVars: []string{"SETTINGS_TRACING_COLLECTOR"},
|
||||
Destination: &cfg.Tracing.Collector,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-service",
|
||||
Value: "settings",
|
||||
Usage: "Service name for tracing",
|
||||
EnvVars: []string{"SETTINGS_TRACING_SERVICE"},
|
||||
Destination: &cfg.Tracing.Service,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "debug-addr",
|
||||
Value: "0.0.0.0:9194",
|
||||
Usage: "Address to bind debug server",
|
||||
EnvVars: []string{"SETTINGS_DEBUG_ADDR"},
|
||||
Destination: &cfg.Debug.Addr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "debug-token",
|
||||
Value: "",
|
||||
Usage: "Token to grant metrics access",
|
||||
EnvVars: []string{"SETTINGS_DEBUG_TOKEN"},
|
||||
Destination: &cfg.Debug.Token,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug-pprof",
|
||||
Usage: "Enable pprof debugging",
|
||||
EnvVars: []string{"SETTINGS_DEBUG_PPROF"},
|
||||
Destination: &cfg.Debug.Pprof,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug-zpages",
|
||||
Usage: "Enable zpages debugging",
|
||||
EnvVars: []string{"SETTINGS_DEBUG_ZPAGES"},
|
||||
Destination: &cfg.Debug.Zpages,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-addr",
|
||||
Value: "0.0.0.0:9190",
|
||||
Usage: "Address to bind http server",
|
||||
EnvVars: []string{"SETTINGS_HTTP_ADDR"},
|
||||
Destination: &cfg.HTTP.Addr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-namespace",
|
||||
Value: "com.owncloud.web",
|
||||
Usage: "Set the base namespace for the http namespace",
|
||||
EnvVars: []string{"SETTINGS_HTTP_NAMESPACE"},
|
||||
Destination: &cfg.HTTP.Namespace,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-root",
|
||||
Value: "/",
|
||||
Usage: "Root path of http server",
|
||||
EnvVars: []string{"SETTINGS_HTTP_ROOT"},
|
||||
Destination: &cfg.HTTP.Root,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grpc-addr",
|
||||
Value: "0.0.0.0:9191",
|
||||
Usage: "Address to bind grpc server",
|
||||
EnvVars: []string{"SETTINGS_GRPC_ADDR"},
|
||||
Destination: &cfg.GRPC.Addr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset-path",
|
||||
Value: "",
|
||||
Usage: "Path to custom assets",
|
||||
EnvVars: []string{"SETTINGS_ASSET_PATH"},
|
||||
Destination: &cfg.Asset.Path,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grpc-namespace",
|
||||
Value: "com.owncloud.api",
|
||||
Usage: "Set the base namespace for the grpc namespace",
|
||||
EnvVars: []string{"SETTINGS_GRPC_NAMESPACE"},
|
||||
Destination: &cfg.GRPC.Namespace,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "data-path",
|
||||
Value: "/var/tmp/ocis-settings",
|
||||
Usage: "Mount path for the storage",
|
||||
EnvVars: []string{"SETTINGS_DATA_PATH"},
|
||||
Destination: &cfg.Storage.DataPath,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "jwt-secret",
|
||||
Value: "Pive-Fumkiu4",
|
||||
Usage: "Used to create JWT to talk to reva, should equal reva's jwt-secret",
|
||||
EnvVars: []string{"SETTINGS_JWT_SECRET"},
|
||||
Destination: &cfg.TokenManager.JWTSecret,
|
||||
},
|
||||
}
|
||||
}
|
||||
32
settings/pkg/metrics/metrics.go
Normal file
32
settings/pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package metrics
|
||||
|
||||
var (
|
||||
// Namespace defines the namespace for the defines metrics.
|
||||
Namespace = "ocis"
|
||||
|
||||
// Subsystem defines the subsystem for the defines metrics.
|
||||
Subsystem = "settings"
|
||||
)
|
||||
|
||||
// Metrics defines the available metrics of this service.
|
||||
type Metrics struct {
|
||||
// Counter *prometheus.CounterVec
|
||||
}
|
||||
|
||||
// New initializes the available metrics.
|
||||
func New() *Metrics {
|
||||
m := &Metrics{
|
||||
// Counter: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
// Namespace: Namespace,
|
||||
// Subsystem: Subsystem,
|
||||
// Name: "greet_total",
|
||||
// Help: "How many greeting requests processed",
|
||||
// }, []string{}),
|
||||
}
|
||||
|
||||
// prometheus.Register(
|
||||
// m.Counter,
|
||||
// )
|
||||
|
||||
return m
|
||||
}
|
||||
186
settings/pkg/proto/v0/settings.mock.go
Normal file
186
settings/pkg/proto/v0/settings.mock.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package proto
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/micro/go-micro/v2/client"
|
||||
)
|
||||
|
||||
// MockBundleService can be used to write tests against the bundle service.
|
||||
/*
|
||||
To create a mock overwrite the functions of an instance like this:
|
||||
|
||||
```go
|
||||
func mockBundleSvc(returnErr bool) proto.BundleService {
|
||||
if returnErr {
|
||||
return &proto.MockBundleService{
|
||||
ListBundlesFunc: func(ctx context.Context, in *proto.ListBundlesRequest, opts ...client.CallOption) (out *proto.ListBundlesResponse, err error) {
|
||||
return nil, fmt.Errorf("error returned by mockBundleSvc LIST")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &proto.MockBundleService{
|
||||
ListBundlesFunc: func(ctx context.Context, in *proto.ListBundlesRequest, opts ...client.CallOption) (out *proto.ListBundlesResponse, err error) {
|
||||
return &proto.ListBundlesResponse{
|
||||
Bundles: []*proto.Bundle{
|
||||
{
|
||||
Id: "hello-there",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
*/
|
||||
type MockBundleService struct {
|
||||
ListBundlesFunc func(ctx context.Context, req *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error)
|
||||
GetBundleFunc func(ctx context.Context, req *GetBundleRequest, opts ...client.CallOption) (*GetBundleResponse, error)
|
||||
SaveBundleFunc func(ctx context.Context, req *SaveBundleRequest, opts ...client.CallOption) (*SaveBundleResponse, error)
|
||||
AddSettingToBundleFunc func(ctx context.Context, req *AddSettingToBundleRequest, opts ...client.CallOption) (*AddSettingToBundleResponse, error)
|
||||
RemoveSettingFromBundleFunc func(ctx context.Context, req *RemoveSettingFromBundleRequest, opts ...client.CallOption) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
// ListBundles will panic if the function has been called, but not mocked
|
||||
func (m MockBundleService) ListBundles(ctx context.Context, req *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error) {
|
||||
if m.ListBundlesFunc != nil {
|
||||
return m.ListBundlesFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("ListBundlesFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// GetBundle will panic if the function has been called, but not mocked
|
||||
func (m MockBundleService) GetBundle(ctx context.Context, req *GetBundleRequest, opts ...client.CallOption) (*GetBundleResponse, error) {
|
||||
if m.GetBundleFunc != nil {
|
||||
return m.GetBundleFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("GetBundleFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// SaveBundle will panic if the function has been called, but not mocked
|
||||
func (m MockBundleService) SaveBundle(ctx context.Context, req *SaveBundleRequest, opts ...client.CallOption) (*SaveBundleResponse, error) {
|
||||
if m.SaveBundleFunc != nil {
|
||||
return m.SaveBundleFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("SaveBundleFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// AddSettingToBundle will panic if the function has been called, but not mocked
|
||||
func (m MockBundleService) AddSettingToBundle(ctx context.Context, req *AddSettingToBundleRequest, opts ...client.CallOption) (*AddSettingToBundleResponse, error) {
|
||||
if m.AddSettingToBundleFunc != nil {
|
||||
return m.AddSettingToBundleFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("AddSettingToBundleFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// RemoveSettingFromBundle will panic if the function has been called, but not mocked
|
||||
func (m MockBundleService) RemoveSettingFromBundle(ctx context.Context, req *RemoveSettingFromBundleRequest, opts ...client.CallOption) (*empty.Empty, error) {
|
||||
if m.RemoveSettingFromBundleFunc != nil {
|
||||
return m.RemoveSettingFromBundleFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("RemoveSettingFromBundleFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// MockValueService can be used to write tests against the value service.
|
||||
type MockValueService struct {
|
||||
ListValuesFunc func(ctx context.Context, req *ListValuesRequest, opts ...client.CallOption) (*ListValuesResponse, error)
|
||||
GetValueFunc func(ctx context.Context, req *GetValueRequest, opts ...client.CallOption) (*GetValueResponse, error)
|
||||
GetValueByUniqueIdentifiersFunc func(ctx context.Context, req *GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*GetValueResponse, error)
|
||||
SaveValueFunc func(ctx context.Context, req *SaveValueRequest, opts ...client.CallOption) (*SaveValueResponse, error)
|
||||
}
|
||||
|
||||
// ListValues will panic if the function has been called, but not mocked
|
||||
func (m MockValueService) ListValues(ctx context.Context, req *ListValuesRequest, opts ...client.CallOption) (*ListValuesResponse, error) {
|
||||
if m.ListValuesFunc != nil {
|
||||
return m.ListValuesFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("ListValuesFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// GetValue will panic if the function has been called, but not mocked
|
||||
func (m MockValueService) GetValue(ctx context.Context, req *GetValueRequest, opts ...client.CallOption) (*GetValueResponse, error) {
|
||||
if m.GetValueFunc != nil {
|
||||
return m.GetValueFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("GetValueFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// GetValueByUniqueIdentifiers will panic if the function has been called, but not mocked
|
||||
func (m MockValueService) GetValueByUniqueIdentifiers(ctx context.Context, req *GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*GetValueResponse, error) {
|
||||
if m.GetValueByUniqueIdentifiersFunc != nil {
|
||||
return m.GetValueByUniqueIdentifiersFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("GetValueByUniqueIdentifiersFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// SaveValue will panic if the function has been called, but not mocked
|
||||
func (m MockValueService) SaveValue(ctx context.Context, req *SaveValueRequest, opts ...client.CallOption) (*SaveValueResponse, error) {
|
||||
if m.SaveValueFunc != nil {
|
||||
return m.SaveValueFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("SaveValueFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// MockRoleService will panic if the function has been called, but not mocked
|
||||
type MockRoleService struct {
|
||||
ListRolesFunc func(ctx context.Context, req *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error)
|
||||
ListRoleAssignmentsFunc func(ctx context.Context, req *ListRoleAssignmentsRequest, opts ...client.CallOption) (*ListRoleAssignmentsResponse, error)
|
||||
AssignRoleToUserFunc func(ctx context.Context, req *AssignRoleToUserRequest, opts ...client.CallOption) (*AssignRoleToUserResponse, error)
|
||||
RemoveRoleFromUserFunc func(ctx context.Context, req *RemoveRoleFromUserRequest, opts ...client.CallOption) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
// ListRoles will panic if the function has been called, but not mocked
|
||||
func (m MockRoleService) ListRoles(ctx context.Context, req *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error) {
|
||||
if m.ListRolesFunc != nil {
|
||||
return m.ListRolesFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("ListRolesFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// ListRoleAssignments will panic if the function has been called, but not mocked
|
||||
func (m MockRoleService) ListRoleAssignments(ctx context.Context, req *ListRoleAssignmentsRequest, opts ...client.CallOption) (*ListRoleAssignmentsResponse, error) {
|
||||
if m.ListRoleAssignmentsFunc != nil {
|
||||
return m.ListRoleAssignmentsFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("ListRoleAssignmentsFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// AssignRoleToUser will panic if the function has been called, but not mocked
|
||||
func (m MockRoleService) AssignRoleToUser(ctx context.Context, req *AssignRoleToUserRequest, opts ...client.CallOption) (*AssignRoleToUserResponse, error) {
|
||||
if m.AssignRoleToUserFunc != nil {
|
||||
return m.AssignRoleToUserFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("AssignRoleToUserFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// RemoveRoleFromUser will panic if the function has been called, but not mocked
|
||||
func (m MockRoleService) RemoveRoleFromUser(ctx context.Context, req *RemoveRoleFromUserRequest, opts ...client.CallOption) (*empty.Empty, error) {
|
||||
if m.RemoveRoleFromUserFunc != nil {
|
||||
return m.RemoveRoleFromUserFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("RemoveRoleFromUserFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// MockPermissionService will panic if the function has been called, but not mocked
|
||||
type MockPermissionService struct {
|
||||
ListPermissionsByResourceFunc func(ctx context.Context, req *ListPermissionsByResourceRequest, opts ...client.CallOption) (*ListPermissionsByResourceResponse, error)
|
||||
GetPermissionByIDFunc func(ctx context.Context, req *GetPermissionByIDRequest, opts ...client.CallOption) (*GetPermissionByIDResponse, error)
|
||||
}
|
||||
|
||||
// ListPermissionsByResource will panic if the function has been called, but not mocked
|
||||
func (m MockPermissionService) ListPermissionsByResource(ctx context.Context, req *ListPermissionsByResourceRequest, opts ...client.CallOption) (*ListPermissionsByResourceResponse, error) {
|
||||
if m.ListPermissionsByResourceFunc != nil {
|
||||
return m.ListPermissionsByResourceFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("ListPermissionsByResourceFunc was called in test but not mocked")
|
||||
}
|
||||
|
||||
// GetPermissionByID will panic if the function has been called, but not mocked
|
||||
func (m MockPermissionService) GetPermissionByID(ctx context.Context, req *GetPermissionByIDRequest, opts ...client.CallOption) (*GetPermissionByIDResponse, error) {
|
||||
if m.GetPermissionByIDFunc != nil {
|
||||
return m.GetPermissionByIDFunc(ctx, req, opts...)
|
||||
}
|
||||
panic("GetPermissionByIDFunc was called in test but not mocked")
|
||||
}
|
||||
3805
settings/pkg/proto/v0/settings.pb.go
Normal file
3805
settings/pkg/proto/v0/settings.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
674
settings/pkg/proto/v0/settings.pb.micro.go
Normal file
674
settings/pkg/proto/v0/settings.pb.micro.go
Normal file
@@ -0,0 +1,674 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: settings.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
empty "github.com/golang/protobuf/ptypes/empty"
|
||||
_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/go-micro/v2/api"
|
||||
client "github.com/micro/go-micro/v2/client"
|
||||
server "github.com/micro/go-micro/v2/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for BundleService service
|
||||
|
||||
func NewBundleServiceEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
&api.Endpoint{
|
||||
Name: "BundleService.SaveBundle",
|
||||
Path: []string{"/api/v0/settings/bundle-save"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "BundleService.GetBundle",
|
||||
Path: []string{"/api/v0/settings/bundle-get"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "BundleService.ListBundles",
|
||||
Path: []string{"/api/v0/settings/bundles-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "BundleService.AddSettingToBundle",
|
||||
Path: []string{"/api/v0/settings/bundles-add-setting"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "BundleService.RemoveSettingFromBundle",
|
||||
Path: []string{"/api/v0/settings/bundles-remove-setting"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for BundleService service
|
||||
|
||||
type BundleService interface {
|
||||
SaveBundle(ctx context.Context, in *SaveBundleRequest, opts ...client.CallOption) (*SaveBundleResponse, error)
|
||||
GetBundle(ctx context.Context, in *GetBundleRequest, opts ...client.CallOption) (*GetBundleResponse, error)
|
||||
ListBundles(ctx context.Context, in *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error)
|
||||
AddSettingToBundle(ctx context.Context, in *AddSettingToBundleRequest, opts ...client.CallOption) (*AddSettingToBundleResponse, error)
|
||||
RemoveSettingFromBundle(ctx context.Context, in *RemoveSettingFromBundleRequest, opts ...client.CallOption) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
type bundleService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewBundleService(name string, c client.Client) BundleService {
|
||||
return &bundleService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *bundleService) SaveBundle(ctx context.Context, in *SaveBundleRequest, opts ...client.CallOption) (*SaveBundleResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "BundleService.SaveBundle", in)
|
||||
out := new(SaveBundleResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bundleService) GetBundle(ctx context.Context, in *GetBundleRequest, opts ...client.CallOption) (*GetBundleResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "BundleService.GetBundle", in)
|
||||
out := new(GetBundleResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bundleService) ListBundles(ctx context.Context, in *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "BundleService.ListBundles", in)
|
||||
out := new(ListBundlesResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bundleService) AddSettingToBundle(ctx context.Context, in *AddSettingToBundleRequest, opts ...client.CallOption) (*AddSettingToBundleResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "BundleService.AddSettingToBundle", in)
|
||||
out := new(AddSettingToBundleResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bundleService) RemoveSettingFromBundle(ctx context.Context, in *RemoveSettingFromBundleRequest, opts ...client.CallOption) (*empty.Empty, error) {
|
||||
req := c.c.NewRequest(c.name, "BundleService.RemoveSettingFromBundle", in)
|
||||
out := new(empty.Empty)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for BundleService service
|
||||
|
||||
type BundleServiceHandler interface {
|
||||
SaveBundle(context.Context, *SaveBundleRequest, *SaveBundleResponse) error
|
||||
GetBundle(context.Context, *GetBundleRequest, *GetBundleResponse) error
|
||||
ListBundles(context.Context, *ListBundlesRequest, *ListBundlesResponse) error
|
||||
AddSettingToBundle(context.Context, *AddSettingToBundleRequest, *AddSettingToBundleResponse) error
|
||||
RemoveSettingFromBundle(context.Context, *RemoveSettingFromBundleRequest, *empty.Empty) error
|
||||
}
|
||||
|
||||
func RegisterBundleServiceHandler(s server.Server, hdlr BundleServiceHandler, opts ...server.HandlerOption) error {
|
||||
type bundleService interface {
|
||||
SaveBundle(ctx context.Context, in *SaveBundleRequest, out *SaveBundleResponse) error
|
||||
GetBundle(ctx context.Context, in *GetBundleRequest, out *GetBundleResponse) error
|
||||
ListBundles(ctx context.Context, in *ListBundlesRequest, out *ListBundlesResponse) error
|
||||
AddSettingToBundle(ctx context.Context, in *AddSettingToBundleRequest, out *AddSettingToBundleResponse) error
|
||||
RemoveSettingFromBundle(ctx context.Context, in *RemoveSettingFromBundleRequest, out *empty.Empty) error
|
||||
}
|
||||
type BundleService struct {
|
||||
bundleService
|
||||
}
|
||||
h := &bundleServiceHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "BundleService.SaveBundle",
|
||||
Path: []string{"/api/v0/settings/bundle-save"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "BundleService.GetBundle",
|
||||
Path: []string{"/api/v0/settings/bundle-get"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "BundleService.ListBundles",
|
||||
Path: []string{"/api/v0/settings/bundles-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "BundleService.AddSettingToBundle",
|
||||
Path: []string{"/api/v0/settings/bundles-add-setting"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "BundleService.RemoveSettingFromBundle",
|
||||
Path: []string{"/api/v0/settings/bundles-remove-setting"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&BundleService{h}, opts...))
|
||||
}
|
||||
|
||||
type bundleServiceHandler struct {
|
||||
BundleServiceHandler
|
||||
}
|
||||
|
||||
func (h *bundleServiceHandler) SaveBundle(ctx context.Context, in *SaveBundleRequest, out *SaveBundleResponse) error {
|
||||
return h.BundleServiceHandler.SaveBundle(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *bundleServiceHandler) GetBundle(ctx context.Context, in *GetBundleRequest, out *GetBundleResponse) error {
|
||||
return h.BundleServiceHandler.GetBundle(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *bundleServiceHandler) ListBundles(ctx context.Context, in *ListBundlesRequest, out *ListBundlesResponse) error {
|
||||
return h.BundleServiceHandler.ListBundles(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *bundleServiceHandler) AddSettingToBundle(ctx context.Context, in *AddSettingToBundleRequest, out *AddSettingToBundleResponse) error {
|
||||
return h.BundleServiceHandler.AddSettingToBundle(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *bundleServiceHandler) RemoveSettingFromBundle(ctx context.Context, in *RemoveSettingFromBundleRequest, out *empty.Empty) error {
|
||||
return h.BundleServiceHandler.RemoveSettingFromBundle(ctx, in, out)
|
||||
}
|
||||
|
||||
// Api Endpoints for ValueService service
|
||||
|
||||
func NewValueServiceEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
&api.Endpoint{
|
||||
Name: "ValueService.SaveValue",
|
||||
Path: []string{"/api/v0/settings/values-save"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "ValueService.GetValue",
|
||||
Path: []string{"/api/v0/settings/values-get"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "ValueService.ListValues",
|
||||
Path: []string{"/api/v0/settings/values-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "ValueService.GetValueByUniqueIdentifiers",
|
||||
Path: []string{"/api/v0/settings/values-get-by-unique-identifiers"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for ValueService service
|
||||
|
||||
type ValueService interface {
|
||||
SaveValue(ctx context.Context, in *SaveValueRequest, opts ...client.CallOption) (*SaveValueResponse, error)
|
||||
GetValue(ctx context.Context, in *GetValueRequest, opts ...client.CallOption) (*GetValueResponse, error)
|
||||
ListValues(ctx context.Context, in *ListValuesRequest, opts ...client.CallOption) (*ListValuesResponse, error)
|
||||
GetValueByUniqueIdentifiers(ctx context.Context, in *GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*GetValueResponse, error)
|
||||
}
|
||||
|
||||
type valueService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewValueService(name string, c client.Client) ValueService {
|
||||
return &valueService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *valueService) SaveValue(ctx context.Context, in *SaveValueRequest, opts ...client.CallOption) (*SaveValueResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "ValueService.SaveValue", in)
|
||||
out := new(SaveValueResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *valueService) GetValue(ctx context.Context, in *GetValueRequest, opts ...client.CallOption) (*GetValueResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "ValueService.GetValue", in)
|
||||
out := new(GetValueResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *valueService) ListValues(ctx context.Context, in *ListValuesRequest, opts ...client.CallOption) (*ListValuesResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "ValueService.ListValues", in)
|
||||
out := new(ListValuesResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *valueService) GetValueByUniqueIdentifiers(ctx context.Context, in *GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*GetValueResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "ValueService.GetValueByUniqueIdentifiers", in)
|
||||
out := new(GetValueResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for ValueService service
|
||||
|
||||
type ValueServiceHandler interface {
|
||||
SaveValue(context.Context, *SaveValueRequest, *SaveValueResponse) error
|
||||
GetValue(context.Context, *GetValueRequest, *GetValueResponse) error
|
||||
ListValues(context.Context, *ListValuesRequest, *ListValuesResponse) error
|
||||
GetValueByUniqueIdentifiers(context.Context, *GetValueByUniqueIdentifiersRequest, *GetValueResponse) error
|
||||
}
|
||||
|
||||
func RegisterValueServiceHandler(s server.Server, hdlr ValueServiceHandler, opts ...server.HandlerOption) error {
|
||||
type valueService interface {
|
||||
SaveValue(ctx context.Context, in *SaveValueRequest, out *SaveValueResponse) error
|
||||
GetValue(ctx context.Context, in *GetValueRequest, out *GetValueResponse) error
|
||||
ListValues(ctx context.Context, in *ListValuesRequest, out *ListValuesResponse) error
|
||||
GetValueByUniqueIdentifiers(ctx context.Context, in *GetValueByUniqueIdentifiersRequest, out *GetValueResponse) error
|
||||
}
|
||||
type ValueService struct {
|
||||
valueService
|
||||
}
|
||||
h := &valueServiceHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "ValueService.SaveValue",
|
||||
Path: []string{"/api/v0/settings/values-save"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "ValueService.GetValue",
|
||||
Path: []string{"/api/v0/settings/values-get"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "ValueService.ListValues",
|
||||
Path: []string{"/api/v0/settings/values-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "ValueService.GetValueByUniqueIdentifiers",
|
||||
Path: []string{"/api/v0/settings/values-get-by-unique-identifiers"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&ValueService{h}, opts...))
|
||||
}
|
||||
|
||||
type valueServiceHandler struct {
|
||||
ValueServiceHandler
|
||||
}
|
||||
|
||||
func (h *valueServiceHandler) SaveValue(ctx context.Context, in *SaveValueRequest, out *SaveValueResponse) error {
|
||||
return h.ValueServiceHandler.SaveValue(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *valueServiceHandler) GetValue(ctx context.Context, in *GetValueRequest, out *GetValueResponse) error {
|
||||
return h.ValueServiceHandler.GetValue(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *valueServiceHandler) ListValues(ctx context.Context, in *ListValuesRequest, out *ListValuesResponse) error {
|
||||
return h.ValueServiceHandler.ListValues(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *valueServiceHandler) GetValueByUniqueIdentifiers(ctx context.Context, in *GetValueByUniqueIdentifiersRequest, out *GetValueResponse) error {
|
||||
return h.ValueServiceHandler.GetValueByUniqueIdentifiers(ctx, in, out)
|
||||
}
|
||||
|
||||
// Api Endpoints for RoleService service
|
||||
|
||||
func NewRoleServiceEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
&api.Endpoint{
|
||||
Name: "RoleService.ListRoles",
|
||||
Path: []string{"/api/v0/settings/roles-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "RoleService.ListRoleAssignments",
|
||||
Path: []string{"/api/v0/settings/assignments-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "RoleService.AssignRoleToUser",
|
||||
Path: []string{"/api/v0/settings/assignments-add"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "RoleService.RemoveRoleFromUser",
|
||||
Path: []string{"/api/v0/settings/assignments-remove"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for RoleService service
|
||||
|
||||
type RoleService interface {
|
||||
ListRoles(ctx context.Context, in *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error)
|
||||
ListRoleAssignments(ctx context.Context, in *ListRoleAssignmentsRequest, opts ...client.CallOption) (*ListRoleAssignmentsResponse, error)
|
||||
AssignRoleToUser(ctx context.Context, in *AssignRoleToUserRequest, opts ...client.CallOption) (*AssignRoleToUserResponse, error)
|
||||
RemoveRoleFromUser(ctx context.Context, in *RemoveRoleFromUserRequest, opts ...client.CallOption) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
type roleService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewRoleService(name string, c client.Client) RoleService {
|
||||
return &roleService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *roleService) ListRoles(ctx context.Context, in *ListBundlesRequest, opts ...client.CallOption) (*ListBundlesResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "RoleService.ListRoles", in)
|
||||
out := new(ListBundlesResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roleService) ListRoleAssignments(ctx context.Context, in *ListRoleAssignmentsRequest, opts ...client.CallOption) (*ListRoleAssignmentsResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "RoleService.ListRoleAssignments", in)
|
||||
out := new(ListRoleAssignmentsResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roleService) AssignRoleToUser(ctx context.Context, in *AssignRoleToUserRequest, opts ...client.CallOption) (*AssignRoleToUserResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "RoleService.AssignRoleToUser", in)
|
||||
out := new(AssignRoleToUserResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roleService) RemoveRoleFromUser(ctx context.Context, in *RemoveRoleFromUserRequest, opts ...client.CallOption) (*empty.Empty, error) {
|
||||
req := c.c.NewRequest(c.name, "RoleService.RemoveRoleFromUser", in)
|
||||
out := new(empty.Empty)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for RoleService service
|
||||
|
||||
type RoleServiceHandler interface {
|
||||
ListRoles(context.Context, *ListBundlesRequest, *ListBundlesResponse) error
|
||||
ListRoleAssignments(context.Context, *ListRoleAssignmentsRequest, *ListRoleAssignmentsResponse) error
|
||||
AssignRoleToUser(context.Context, *AssignRoleToUserRequest, *AssignRoleToUserResponse) error
|
||||
RemoveRoleFromUser(context.Context, *RemoveRoleFromUserRequest, *empty.Empty) error
|
||||
}
|
||||
|
||||
func RegisterRoleServiceHandler(s server.Server, hdlr RoleServiceHandler, opts ...server.HandlerOption) error {
|
||||
type roleService interface {
|
||||
ListRoles(ctx context.Context, in *ListBundlesRequest, out *ListBundlesResponse) error
|
||||
ListRoleAssignments(ctx context.Context, in *ListRoleAssignmentsRequest, out *ListRoleAssignmentsResponse) error
|
||||
AssignRoleToUser(ctx context.Context, in *AssignRoleToUserRequest, out *AssignRoleToUserResponse) error
|
||||
RemoveRoleFromUser(ctx context.Context, in *RemoveRoleFromUserRequest, out *empty.Empty) error
|
||||
}
|
||||
type RoleService struct {
|
||||
roleService
|
||||
}
|
||||
h := &roleServiceHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "RoleService.ListRoles",
|
||||
Path: []string{"/api/v0/settings/roles-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "RoleService.ListRoleAssignments",
|
||||
Path: []string{"/api/v0/settings/assignments-list"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "RoleService.AssignRoleToUser",
|
||||
Path: []string{"/api/v0/settings/assignments-add"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "RoleService.RemoveRoleFromUser",
|
||||
Path: []string{"/api/v0/settings/assignments-remove"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&RoleService{h}, opts...))
|
||||
}
|
||||
|
||||
type roleServiceHandler struct {
|
||||
RoleServiceHandler
|
||||
}
|
||||
|
||||
func (h *roleServiceHandler) ListRoles(ctx context.Context, in *ListBundlesRequest, out *ListBundlesResponse) error {
|
||||
return h.RoleServiceHandler.ListRoles(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *roleServiceHandler) ListRoleAssignments(ctx context.Context, in *ListRoleAssignmentsRequest, out *ListRoleAssignmentsResponse) error {
|
||||
return h.RoleServiceHandler.ListRoleAssignments(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *roleServiceHandler) AssignRoleToUser(ctx context.Context, in *AssignRoleToUserRequest, out *AssignRoleToUserResponse) error {
|
||||
return h.RoleServiceHandler.AssignRoleToUser(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *roleServiceHandler) RemoveRoleFromUser(ctx context.Context, in *RemoveRoleFromUserRequest, out *empty.Empty) error {
|
||||
return h.RoleServiceHandler.RemoveRoleFromUser(ctx, in, out)
|
||||
}
|
||||
|
||||
// Api Endpoints for PermissionService service
|
||||
|
||||
func NewPermissionServiceEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
&api.Endpoint{
|
||||
Name: "PermissionService.ListPermissionsByResource",
|
||||
Path: []string{"/api/v0/settings/permissions-list-by-resource"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
&api.Endpoint{
|
||||
Name: "PermissionService.GetPermissionByID",
|
||||
Path: []string{"/api/v0/settings/permissions-get-by-id"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for PermissionService service
|
||||
|
||||
type PermissionService interface {
|
||||
ListPermissionsByResource(ctx context.Context, in *ListPermissionsByResourceRequest, opts ...client.CallOption) (*ListPermissionsByResourceResponse, error)
|
||||
GetPermissionByID(ctx context.Context, in *GetPermissionByIDRequest, opts ...client.CallOption) (*GetPermissionByIDResponse, error)
|
||||
}
|
||||
|
||||
type permissionService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewPermissionService(name string, c client.Client) PermissionService {
|
||||
return &permissionService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *permissionService) ListPermissionsByResource(ctx context.Context, in *ListPermissionsByResourceRequest, opts ...client.CallOption) (*ListPermissionsByResourceResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "PermissionService.ListPermissionsByResource", in)
|
||||
out := new(ListPermissionsByResourceResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *permissionService) GetPermissionByID(ctx context.Context, in *GetPermissionByIDRequest, opts ...client.CallOption) (*GetPermissionByIDResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "PermissionService.GetPermissionByID", in)
|
||||
out := new(GetPermissionByIDResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for PermissionService service
|
||||
|
||||
type PermissionServiceHandler interface {
|
||||
ListPermissionsByResource(context.Context, *ListPermissionsByResourceRequest, *ListPermissionsByResourceResponse) error
|
||||
GetPermissionByID(context.Context, *GetPermissionByIDRequest, *GetPermissionByIDResponse) error
|
||||
}
|
||||
|
||||
func RegisterPermissionServiceHandler(s server.Server, hdlr PermissionServiceHandler, opts ...server.HandlerOption) error {
|
||||
type permissionService interface {
|
||||
ListPermissionsByResource(ctx context.Context, in *ListPermissionsByResourceRequest, out *ListPermissionsByResourceResponse) error
|
||||
GetPermissionByID(ctx context.Context, in *GetPermissionByIDRequest, out *GetPermissionByIDResponse) error
|
||||
}
|
||||
type PermissionService struct {
|
||||
permissionService
|
||||
}
|
||||
h := &permissionServiceHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "PermissionService.ListPermissionsByResource",
|
||||
Path: []string{"/api/v0/settings/permissions-list-by-resource"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "PermissionService.GetPermissionByID",
|
||||
Path: []string{"/api/v0/settings/permissions-get-by-id"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&PermissionService{h}, opts...))
|
||||
}
|
||||
|
||||
type permissionServiceHandler struct {
|
||||
PermissionServiceHandler
|
||||
}
|
||||
|
||||
func (h *permissionServiceHandler) ListPermissionsByResource(ctx context.Context, in *ListPermissionsByResourceRequest, out *ListPermissionsByResourceResponse) error {
|
||||
return h.PermissionServiceHandler.ListPermissionsByResource(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *permissionServiceHandler) GetPermissionByID(ctx context.Context, in *GetPermissionByIDRequest, out *GetPermissionByIDResponse) error {
|
||||
return h.PermissionServiceHandler.GetPermissionByID(ctx, in, out)
|
||||
}
|
||||
1684
settings/pkg/proto/v0/settings.pb.micro_test.go
Normal file
1684
settings/pkg/proto/v0/settings.pb.micro_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1933
settings/pkg/proto/v0/settings.pb.web.go
Normal file
1933
settings/pkg/proto/v0/settings.pb.web.go
Normal file
File diff suppressed because it is too large
Load Diff
414
settings/pkg/proto/v0/settings.proto
Normal file
414
settings/pkg/proto/v0/settings.proto
Normal file
@@ -0,0 +1,414 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
option go_package = "pkg/proto/v0;proto";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Settings";
|
||||
version: "1.0";
|
||||
contact: {
|
||||
name: "ownCloud GmbH";
|
||||
url: "https://github.com/owncloud/ocis-settings";
|
||||
email: "support@owncloud.com";
|
||||
};
|
||||
license: {
|
||||
name: "Apache-2.0";
|
||||
url: "https://github.com/owncloud/ocis-settings/blob/master/LICENSE";
|
||||
};
|
||||
};
|
||||
schemes: HTTP;
|
||||
schemes: HTTPS;
|
||||
consumes: "application/json";
|
||||
produces: "application/json";
|
||||
external_docs: {
|
||||
description: "Developer Manual";
|
||||
url: "http://owncloud.github.io/extensions/ocis_settings/";
|
||||
};
|
||||
};
|
||||
|
||||
service BundleService {
|
||||
rpc SaveBundle(SaveBundleRequest) returns (SaveBundleResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/bundle-save",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc GetBundle(GetBundleRequest) returns (GetBundleResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/bundle-get",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc ListBundles(ListBundlesRequest) returns (ListBundlesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/bundles-list",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc AddSettingToBundle(AddSettingToBundleRequest) returns (AddSettingToBundleResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/bundles-add-setting",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc RemoveSettingFromBundle(RemoveSettingFromBundleRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/bundles-remove-setting",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service ValueService {
|
||||
rpc SaveValue(SaveValueRequest) returns (SaveValueResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/values-save",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc GetValue(GetValueRequest) returns (GetValueResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/values-get",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc ListValues(ListValuesRequest) returns (ListValuesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/values-list",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc GetValueByUniqueIdentifiers(GetValueByUniqueIdentifiersRequest) returns (GetValueResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/values-get-by-unique-identifiers",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service RoleService {
|
||||
rpc ListRoles(ListBundlesRequest) returns (ListBundlesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/roles-list",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc ListRoleAssignments(ListRoleAssignmentsRequest) returns (ListRoleAssignmentsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/assignments-list",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc AssignRoleToUser(AssignRoleToUserRequest) returns (AssignRoleToUserResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/assignments-add",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc RemoveRoleFromUser(RemoveRoleFromUserRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/assignments-remove",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service PermissionService {
|
||||
rpc ListPermissionsByResource(ListPermissionsByResourceRequest) returns (ListPermissionsByResourceResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/permissions-list-by-resource",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc GetPermissionByID(GetPermissionByIDRequest) returns (GetPermissionByIDResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/settings/permissions-get-by-id",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
// requests and responses for settings bundles
|
||||
// ---
|
||||
message SaveBundleRequest {
|
||||
Bundle bundle = 1;
|
||||
}
|
||||
|
||||
message SaveBundleResponse {
|
||||
Bundle bundle = 1;
|
||||
}
|
||||
|
||||
message GetBundleRequest {
|
||||
string bundle_id = 1;
|
||||
}
|
||||
|
||||
message GetBundleResponse {
|
||||
Bundle bundle = 1;
|
||||
}
|
||||
|
||||
message ListBundlesRequest {
|
||||
repeated string bundle_ids = 1;
|
||||
}
|
||||
|
||||
message ListBundlesResponse {
|
||||
repeated Bundle bundles = 1;
|
||||
}
|
||||
|
||||
message AddSettingToBundleRequest {
|
||||
string bundle_id = 1;
|
||||
Setting setting = 2;
|
||||
}
|
||||
|
||||
message AddSettingToBundleResponse {
|
||||
Setting setting = 1;
|
||||
}
|
||||
|
||||
message RemoveSettingFromBundleRequest {
|
||||
string bundle_id = 1;
|
||||
string setting_id = 2;
|
||||
}
|
||||
|
||||
// ---
|
||||
// requests and responses for settings values
|
||||
// ---
|
||||
|
||||
message SaveValueRequest {
|
||||
Value value = 1;
|
||||
}
|
||||
|
||||
message SaveValueResponse {
|
||||
ValueWithIdentifier value = 1;
|
||||
}
|
||||
|
||||
message GetValueRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetValueResponse {
|
||||
ValueWithIdentifier value = 1;
|
||||
}
|
||||
|
||||
message ListValuesRequest {
|
||||
string bundle_id = 1;
|
||||
string account_uuid = 2;
|
||||
}
|
||||
|
||||
message ListValuesResponse {
|
||||
repeated ValueWithIdentifier values = 1;
|
||||
}
|
||||
|
||||
message GetValueByUniqueIdentifiersRequest{
|
||||
string account_uuid = 1;
|
||||
string setting_id = 2;
|
||||
}
|
||||
|
||||
message ValueWithIdentifier {
|
||||
Identifier identifier = 1;
|
||||
Value value = 2;
|
||||
}
|
||||
|
||||
message Identifier {
|
||||
string extension = 1;
|
||||
string bundle = 2;
|
||||
string setting = 3;
|
||||
}
|
||||
|
||||
// --
|
||||
// requests and responses for role assignments
|
||||
// ---
|
||||
|
||||
message ListRoleAssignmentsRequest {
|
||||
string account_uuid = 1;
|
||||
}
|
||||
|
||||
message ListRoleAssignmentsResponse {
|
||||
repeated UserRoleAssignment assignments = 1;
|
||||
}
|
||||
|
||||
message AssignRoleToUserRequest {
|
||||
string account_uuid = 1;
|
||||
// the role_id is a bundle_id internally
|
||||
string role_id = 2;
|
||||
}
|
||||
|
||||
message AssignRoleToUserResponse {
|
||||
UserRoleAssignment assignment = 1;
|
||||
}
|
||||
|
||||
message RemoveRoleFromUserRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message UserRoleAssignment {
|
||||
// id is generated upon saving the assignment
|
||||
string id = 1;
|
||||
string account_uuid = 2;
|
||||
// the role_id is a bundle_id internally
|
||||
string role_id = 3;
|
||||
}
|
||||
|
||||
// --
|
||||
// requests and responses for permissions
|
||||
// ---
|
||||
|
||||
message ListPermissionsByResourceRequest {
|
||||
Resource resource = 1;
|
||||
}
|
||||
|
||||
message ListPermissionsByResourceResponse {
|
||||
repeated Permission permissions = 1;
|
||||
}
|
||||
|
||||
message GetPermissionByIDRequest {
|
||||
string permission_id = 1;
|
||||
}
|
||||
|
||||
message GetPermissionByIDResponse {
|
||||
Permission permission = 1;
|
||||
}
|
||||
|
||||
// ---
|
||||
// resource payloads
|
||||
// ---
|
||||
|
||||
message Resource {
|
||||
enum Type {
|
||||
TYPE_UNKNOWN = 0;
|
||||
TYPE_SYSTEM = 1;
|
||||
TYPE_FILE = 2;
|
||||
TYPE_SHARE = 3;
|
||||
TYPE_SETTING = 4;
|
||||
TYPE_BUNDLE = 5;
|
||||
TYPE_USER = 6;
|
||||
TYPE_GROUP = 7;
|
||||
}
|
||||
Type type = 1;
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
// ---
|
||||
// payloads for bundles
|
||||
// ---
|
||||
|
||||
message Bundle {
|
||||
enum Type {
|
||||
TYPE_UNKNOWN = 0;
|
||||
TYPE_DEFAULT = 1;
|
||||
TYPE_ROLE = 2;
|
||||
}
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
Type type = 3;
|
||||
string extension = 4;
|
||||
string display_name = 5;
|
||||
repeated Setting settings = 6;
|
||||
Resource resource = 7;
|
||||
}
|
||||
|
||||
message Setting {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string display_name = 3;
|
||||
string description = 4;
|
||||
oneof value {
|
||||
Int int_value = 5;
|
||||
String string_value = 6;
|
||||
Bool bool_value = 7;
|
||||
SingleChoiceList single_choice_value = 8;
|
||||
MultiChoiceList multi_choice_value = 9;
|
||||
Permission permission_value = 10;
|
||||
}
|
||||
Resource resource = 11;
|
||||
}
|
||||
|
||||
message Int {
|
||||
int64 default = 1;
|
||||
int64 min = 2;
|
||||
int64 max = 3;
|
||||
int64 step = 4;
|
||||
string placeholder = 5;
|
||||
}
|
||||
|
||||
message String {
|
||||
string default = 1;
|
||||
bool required = 2;
|
||||
int32 min_length = 3;
|
||||
int32 max_length = 4;
|
||||
string placeholder = 5;
|
||||
}
|
||||
|
||||
message Bool {
|
||||
bool default = 1;
|
||||
string label = 2;
|
||||
}
|
||||
|
||||
message SingleChoiceList {
|
||||
repeated ListOption options = 1;
|
||||
}
|
||||
|
||||
message MultiChoiceList {
|
||||
repeated ListOption options = 1;
|
||||
}
|
||||
|
||||
message ListOption {
|
||||
ListOptionValue value = 1;
|
||||
bool default = 2;
|
||||
string display_value = 3;
|
||||
}
|
||||
|
||||
message Permission {
|
||||
enum Operation {
|
||||
OPERATION_UNKNOWN = 0;
|
||||
OPERATION_CREATE = 1;
|
||||
OPERATION_READ = 2;
|
||||
OPERATION_UPDATE = 3;
|
||||
OPERATION_DELETE = 4;
|
||||
OPERATION_WRITE = 5;// WRITE is a combination of CREATE and UPDATE
|
||||
OPERATION_READWRITE = 6;// READWRITE is a combination of READ and WRITE
|
||||
}
|
||||
Operation operation = 1;
|
||||
enum Constraint {
|
||||
CONSTRAINT_UNKNOWN = 0;
|
||||
CONSTRAINT_OWN = 1;
|
||||
CONSTRAINT_SHARED = 2;
|
||||
CONSTRAINT_ALL = 3;
|
||||
}
|
||||
Constraint constraint = 2;
|
||||
}
|
||||
|
||||
// ---
|
||||
// payloads for values
|
||||
// ---
|
||||
|
||||
message Value {
|
||||
// id is the id of the Value. It is generated on saving it.
|
||||
string id = 1;
|
||||
string bundle_id = 2;
|
||||
// setting_id is the id of the setting from within its bundle.
|
||||
string setting_id = 3;
|
||||
string account_uuid = 4;
|
||||
Resource resource = 5;
|
||||
oneof value {
|
||||
bool bool_value = 6;
|
||||
int64 int_value = 7;
|
||||
string string_value = 8;
|
||||
ListValue list_value = 9;
|
||||
}
|
||||
}
|
||||
|
||||
message ListValue {
|
||||
repeated ListOptionValue values = 1;
|
||||
}
|
||||
|
||||
message ListOptionValue {
|
||||
oneof option {
|
||||
string string_value = 1;
|
||||
int64 int_value = 2;
|
||||
}
|
||||
}
|
||||
1113
settings/pkg/proto/v0/settings.swagger.json
Normal file
1113
settings/pkg/proto/v0/settings.swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
50
settings/pkg/server/debug/option.go
Normal file
50
settings/pkg/server/debug/option.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/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
|
||||
}
|
||||
}
|
||||
51
settings/pkg/server/debug/server.go
Normal file
51
settings/pkg/server/debug/server.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/service/debug"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/version"
|
||||
)
|
||||
|
||||
// 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("settings"),
|
||||
debug.Version(version.String),
|
||||
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)),
|
||||
), nil
|
||||
}
|
||||
|
||||
// health implements the health check.
|
||||
func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO(tboerger): check if services are up and running
|
||||
|
||||
_, _ = io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
}
|
||||
}
|
||||
|
||||
// ready implements the ready check.
|
||||
func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO(tboerger): check if services are up and running
|
||||
|
||||
_, _ = io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
}
|
||||
}
|
||||
76
settings/pkg/server/grpc/option.go
Normal file
76
settings/pkg/server/grpc/option.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/metrics"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Name string
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Name provides a name for the service.
|
||||
func Name(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = 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
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics provides a function to set the metrics option.
|
||||
func Metrics(val *metrics.Metrics) Option {
|
||||
return func(o *Options) {
|
||||
o.Metrics = 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...)
|
||||
}
|
||||
}
|
||||
40
settings/pkg/server/grpc/server.go
Normal file
40
settings/pkg/server/grpc/server.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-pkg/v2/service/grpc"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
svc "github.com/owncloud/ocis-settings/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-settings/pkg/version"
|
||||
)
|
||||
|
||||
// Server initializes a new go-micro service ready to run
|
||||
func Server(opts ...Option) grpc.Service {
|
||||
options := newOptions(opts...)
|
||||
|
||||
service := grpc.NewService(
|
||||
grpc.Logger(options.Logger),
|
||||
grpc.Name(options.Name),
|
||||
grpc.Version(version.String),
|
||||
grpc.Address(options.Config.GRPC.Addr),
|
||||
grpc.Namespace(options.Config.GRPC.Namespace),
|
||||
grpc.Context(options.Context),
|
||||
grpc.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
handle := svc.NewService(options.Config, options.Logger)
|
||||
if err := proto.RegisterBundleServiceHandler(service.Server(), handle); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register Bundle service handler")
|
||||
}
|
||||
if err := proto.RegisterValueServiceHandler(service.Server(), handle); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register Value service handler")
|
||||
}
|
||||
if err := proto.RegisterRoleServiceHandler(service.Server(), handle); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register Role service handler")
|
||||
}
|
||||
if err := proto.RegisterPermissionServiceHandler(service.Server(), handle); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register Permission service handler")
|
||||
}
|
||||
|
||||
service.Init()
|
||||
return service
|
||||
}
|
||||
76
settings/pkg/server/http/option.go
Normal file
76
settings/pkg/server/http/option.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/metrics"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Name string
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Name provides a name for the service.
|
||||
func Name(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = 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
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics provides a function to set the metrics option.
|
||||
func Metrics(val *metrics.Metrics) Option {
|
||||
return func(o *Options) {
|
||||
o.Metrics = 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...)
|
||||
}
|
||||
}
|
||||
81
settings/pkg/server/http/server.go
Normal file
81
settings/pkg/server/http/server.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/owncloud/ocis-pkg/v2/account"
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
"github.com/owncloud/ocis-pkg/v2/service/http"
|
||||
"github.com/owncloud/ocis-settings/pkg/assets"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
svc "github.com/owncloud/ocis-settings/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-settings/pkg/version"
|
||||
)
|
||||
|
||||
// Server initializes the http service and server.
|
||||
func Server(opts ...Option) http.Service {
|
||||
options := newOptions(opts...)
|
||||
|
||||
service := http.NewService(
|
||||
http.Logger(options.Logger),
|
||||
http.Name(options.Name),
|
||||
http.Version(version.String),
|
||||
http.Address(options.Config.HTTP.Addr),
|
||||
http.Namespace(options.Config.HTTP.Namespace),
|
||||
http.Context(options.Context),
|
||||
http.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
handle := svc.NewService(options.Config, options.Logger)
|
||||
|
||||
{
|
||||
handle = svc.NewInstrument(handle, options.Metrics)
|
||||
handle = svc.NewLogging(handle, options.Logger)
|
||||
handle = svc.NewTracing(handle)
|
||||
}
|
||||
|
||||
mux := chi.NewMux()
|
||||
|
||||
mux.Use(middleware.RealIP)
|
||||
mux.Use(middleware.RequestID)
|
||||
mux.Use(middleware.Cache)
|
||||
mux.Use(middleware.Cors)
|
||||
mux.Use(middleware.Secure)
|
||||
mux.Use(middleware.ExtractAccountUUID(
|
||||
account.Logger(options.Logger),
|
||||
account.JWTSecret(options.Config.TokenManager.JWTSecret)),
|
||||
)
|
||||
|
||||
mux.Use(middleware.Version(
|
||||
options.Name,
|
||||
version.String,
|
||||
))
|
||||
|
||||
mux.Use(middleware.Logger(
|
||||
options.Logger,
|
||||
))
|
||||
|
||||
mux.Use(middleware.Static(
|
||||
options.Config.HTTP.Root,
|
||||
assets.New(
|
||||
assets.Logger(options.Logger),
|
||||
assets.Config(options.Config),
|
||||
),
|
||||
))
|
||||
|
||||
mux.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
||||
proto.RegisterBundleServiceWeb(r, handle)
|
||||
proto.RegisterValueServiceWeb(r, handle)
|
||||
proto.RegisterRoleServiceWeb(r, handle)
|
||||
proto.RegisterPermissionServiceWeb(r, handle)
|
||||
})
|
||||
|
||||
service.Handle(
|
||||
"/",
|
||||
mux,
|
||||
)
|
||||
|
||||
if err := service.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return service
|
||||
}
|
||||
13
settings/pkg/service/v0/instrument.go
Normal file
13
settings/pkg/service/v0/instrument.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-settings/pkg/metrics"
|
||||
)
|
||||
|
||||
// NewInstrument returns a service that instruments metrics.
|
||||
func NewInstrument(next Service, metrics *metrics.Metrics) Service {
|
||||
return Service{
|
||||
manager: next.manager,
|
||||
config: next.config,
|
||||
}
|
||||
}
|
||||
13
settings/pkg/service/v0/logging.go
Normal file
13
settings/pkg/service/v0/logging.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// NewLogging returns a service that logs messages.
|
||||
func NewLogging(next Service, logger log.Logger) Service {
|
||||
return Service{
|
||||
manager: next.manager,
|
||||
config: next.config,
|
||||
}
|
||||
}
|
||||
39
settings/pkg/service/v0/option.go
Normal file
39
settings/pkg/service/v0/option.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/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
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware provides a function to set the middleware option.
|
||||
func Middleware(val ...func(http.Handler) http.Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.Middleware = val
|
||||
}
|
||||
}
|
||||
57
settings/pkg/service/v0/permissions.go
Normal file
57
settings/pkg/service/v0/permissions.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package svc
|
||||
|
||||
import "github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
|
||||
func (g Service) hasPermission(
|
||||
roleIDs []string,
|
||||
resource *proto.Resource,
|
||||
operations []proto.Permission_Operation,
|
||||
constraint proto.Permission_Constraint,
|
||||
) bool {
|
||||
permissions, err := g.manager.ListPermissionsByResource(resource, roleIDs)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).
|
||||
Str("resource-type", resource.Type.String()).
|
||||
Str("resource-id", resource.Id).
|
||||
Msg("permissions could not be loaded for resource")
|
||||
return false
|
||||
}
|
||||
permissions = getFilteredPermissionsByOperations(permissions, operations)
|
||||
return isConstraintFulfilled(permissions, constraint)
|
||||
}
|
||||
|
||||
// filterPermissionsByOperations returns the subset of the given permissions, where at least one of the given operations is fulfilled.
|
||||
func getFilteredPermissionsByOperations(permissions []*proto.Permission, operations []proto.Permission_Operation) []*proto.Permission {
|
||||
var filteredPermissions []*proto.Permission
|
||||
for _, permission := range permissions {
|
||||
if isAnyOperationFulfilled(permission, operations) {
|
||||
filteredPermissions = append(filteredPermissions, permission)
|
||||
}
|
||||
}
|
||||
return filteredPermissions
|
||||
}
|
||||
|
||||
// isAnyOperationFulfilled checks if the permissions is about any of the operations
|
||||
func isAnyOperationFulfilled(permission *proto.Permission, operations []proto.Permission_Operation) bool {
|
||||
for _, operation := range operations {
|
||||
if operation == permission.Operation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isConstraintFulfilled checks if one of the permissions has the same or a parent of the constraint.
|
||||
// this is only a comparison on ENUM level. More sophisticated checks cannot happen here...
|
||||
func isConstraintFulfilled(permissions []*proto.Permission, constraint proto.Permission_Constraint) bool {
|
||||
for _, permission := range permissions {
|
||||
// comparing enum by order is not a feasible solution, because `SHARED` is not a superset of `OWN`.
|
||||
if permission.Constraint == proto.Permission_CONSTRAINT_ALL {
|
||||
return true
|
||||
}
|
||||
if permission.Constraint != proto.Permission_CONSTRAINT_UNKNOWN && permission.Constraint == constraint {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
388
settings/pkg/service/v0/service.go
Normal file
388
settings/pkg/service/v0/service.go
Normal file
@@ -0,0 +1,388 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
"github.com/micro/go-micro/v2/metadata"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
"github.com/owncloud/ocis-pkg/v2/roles"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-settings/pkg/settings"
|
||||
store "github.com/owncloud/ocis-settings/pkg/store/filesystem"
|
||||
)
|
||||
|
||||
// Service represents a service.
|
||||
type Service struct {
|
||||
id string
|
||||
config *config.Config
|
||||
logger log.Logger
|
||||
manager settings.Manager
|
||||
}
|
||||
|
||||
// NewService returns a service implementation for Service.
|
||||
func NewService(cfg *config.Config, logger log.Logger) Service {
|
||||
service := Service{
|
||||
id: "ocis-settings",
|
||||
config: cfg,
|
||||
logger: logger,
|
||||
manager: store.New(cfg),
|
||||
}
|
||||
service.RegisterDefaultRoles()
|
||||
return service
|
||||
}
|
||||
|
||||
// RegisterDefaultRoles composes default roles and saves them. Skipped if the roles already exist.
|
||||
func (g Service) RegisterDefaultRoles() {
|
||||
// FIXME: we're writing default roles per service start (i.e. twice at the moment, for http and grpc server). has to happen only once.
|
||||
for _, role := range generateBundlesDefaultRoles() {
|
||||
bundleID := role.Extension + "." + role.Id
|
||||
// check if the role already exists
|
||||
bundle, _ := g.manager.ReadBundle(role.Id)
|
||||
if bundle != nil {
|
||||
g.logger.Debug().Str("bundleID", bundleID).Msg("bundle already exists. skipping.")
|
||||
continue
|
||||
}
|
||||
// create the role
|
||||
_, err := g.manager.WriteBundle(role)
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Str("bundleID", bundleID).Msg("failed to register bundle")
|
||||
}
|
||||
g.logger.Debug().Str("bundleID", bundleID).Msg("successfully registered bundle")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check permissions on every request
|
||||
|
||||
// SaveBundle implements the BundleServiceHandler interface
|
||||
func (g Service) SaveBundle(c context.Context, req *proto.SaveBundleRequest, res *proto.SaveBundleResponse) error {
|
||||
cleanUpResource(c, req.Bundle.Resource)
|
||||
if validationError := validateSaveBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.WriteBundle(req.Bundle)
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Bundle = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBundle implements the BundleServiceHandler interface
|
||||
func (g Service) GetBundle(c context.Context, req *proto.GetBundleRequest, res *proto.GetBundleResponse) error {
|
||||
if validationError := validateGetBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
bundle, err := g.manager.ReadBundle(req.BundleId)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
filteredBundle := g.getFilteredBundle(g.getRoleIDs(c), bundle)
|
||||
if len(filteredBundle.Settings) == 0 {
|
||||
err = fmt.Errorf("could not read bundle: %s", req.BundleId)
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Bundle = filteredBundle
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListBundles implements the BundleServiceHandler interface
|
||||
func (g Service) ListBundles(c context.Context, req *proto.ListBundlesRequest, res *proto.ListBundlesResponse) error {
|
||||
// fetch all bundles
|
||||
if validationError := validateListBundles(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
bundles, err := g.manager.ListBundles(proto.Bundle_TYPE_DEFAULT, req.BundleIds)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
roleIDs := g.getRoleIDs(c)
|
||||
|
||||
// filter settings in bundles that are allowed according to roles
|
||||
var filteredBundles []*proto.Bundle
|
||||
for _, bundle := range bundles {
|
||||
filteredBundle := g.getFilteredBundle(roleIDs, bundle)
|
||||
if len(filteredBundle.Settings) > 0 {
|
||||
filteredBundles = append(filteredBundles, filteredBundle)
|
||||
}
|
||||
}
|
||||
|
||||
res.Bundles = filteredBundles
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Service) getFilteredBundle(roleIDs []string, bundle *proto.Bundle) *proto.Bundle {
|
||||
// check if full bundle is whitelisted
|
||||
bundleResource := &proto.Resource{
|
||||
Type: proto.Resource_TYPE_BUNDLE,
|
||||
Id: bundle.Id,
|
||||
}
|
||||
if g.hasPermission(
|
||||
roleIDs,
|
||||
bundleResource,
|
||||
[]proto.Permission_Operation{proto.Permission_OPERATION_READ, proto.Permission_OPERATION_READWRITE},
|
||||
proto.Permission_CONSTRAINT_OWN,
|
||||
) {
|
||||
return bundle
|
||||
}
|
||||
|
||||
// filter settings based on permissions
|
||||
var filteredSettings []*proto.Setting
|
||||
for _, setting := range bundle.Settings {
|
||||
settingResource := &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SETTING,
|
||||
Id: setting.Id,
|
||||
}
|
||||
if g.hasPermission(
|
||||
roleIDs,
|
||||
settingResource,
|
||||
[]proto.Permission_Operation{proto.Permission_OPERATION_READ, proto.Permission_OPERATION_READWRITE},
|
||||
proto.Permission_CONSTRAINT_OWN,
|
||||
) {
|
||||
filteredSettings = append(filteredSettings, setting)
|
||||
}
|
||||
}
|
||||
bundle.Settings = filteredSettings
|
||||
return bundle
|
||||
}
|
||||
|
||||
// AddSettingToBundle implements the BundleServiceHandler interface
|
||||
func (g Service) AddSettingToBundle(c context.Context, req *proto.AddSettingToBundleRequest, res *proto.AddSettingToBundleResponse) error {
|
||||
cleanUpResource(c, req.Setting.Resource)
|
||||
if validationError := validateAddSettingToBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.AddSettingToBundle(req.BundleId, req.Setting)
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Setting = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSettingFromBundle implements the BundleServiceHandler interface
|
||||
func (g Service) RemoveSettingFromBundle(c context.Context, req *proto.RemoveSettingFromBundleRequest, _ *empty.Empty) error {
|
||||
if validationError := validateRemoveSettingFromBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
if err := g.manager.RemoveSettingFromBundle(req.BundleId, req.SettingId); err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveValue implements the ValueServiceHandler interface
|
||||
func (g Service) SaveValue(c context.Context, req *proto.SaveValueRequest, res *proto.SaveValueResponse) error {
|
||||
req.Value.AccountUuid = getValidatedAccountUUID(c, req.Value.AccountUuid)
|
||||
cleanUpResource(c, req.Value.Resource)
|
||||
// TODO: we need to check, if the authenticated user has permission to write the value for the specified resource (e.g. global, file with id xy, ...)
|
||||
if validationError := validateSaveValue(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.WriteValue(req.Value)
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(r)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Value = valueWithIdentifier
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue implements the ValueServiceHandler interface
|
||||
func (g Service) GetValue(c context.Context, req *proto.GetValueRequest, res *proto.GetValueResponse) error {
|
||||
if validationError := validateGetValue(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ReadValue(req.Id)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(r)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Value = valueWithIdentifier
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValueByUniqueIdentifiers implements the ValueService interface
|
||||
func (g Service) GetValueByUniqueIdentifiers(ctx context.Context, req *proto.GetValueByUniqueIdentifiersRequest, res *proto.GetValueResponse) error {
|
||||
if validationError := validateGetValueByUniqueIdentifiers(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
v, err := g.manager.ReadValueByUniqueIdentifiers(req.AccountUuid, req.SettingId)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
|
||||
if v.BundleId != "" {
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(v)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
|
||||
res.Value = valueWithIdentifier
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListValues implements the ValueServiceHandler interface
|
||||
func (g Service) ListValues(c context.Context, req *proto.ListValuesRequest, res *proto.ListValuesResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(c, req.AccountUuid)
|
||||
if validationError := validateListValues(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ListValues(req.BundleId, req.AccountUuid)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
var result []*proto.ValueWithIdentifier
|
||||
for _, value := range r {
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(value)
|
||||
if err == nil {
|
||||
result = append(result, valueWithIdentifier)
|
||||
}
|
||||
}
|
||||
res.Values = result
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRoles implements the RoleServiceHandler interface
|
||||
func (g Service) ListRoles(c context.Context, req *proto.ListBundlesRequest, res *proto.ListBundlesResponse) error {
|
||||
//accountUUID := getValidatedAccountUUID(c, "me")
|
||||
if validationError := validateListRoles(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ListBundles(proto.Bundle_TYPE_ROLE, req.BundleIds)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
// TODO: only allow to list roles when user has account/role/... management permissions
|
||||
res.Bundles = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRoleAssignments implements the RoleServiceHandler interface
|
||||
func (g Service) ListRoleAssignments(c context.Context, req *proto.ListRoleAssignmentsRequest, res *proto.ListRoleAssignmentsResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(c, req.AccountUuid)
|
||||
if validationError := validateListRoleAssignments(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ListRoleAssignments(req.AccountUuid)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Assignments = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssignRoleToUser implements the RoleServiceHandler interface
|
||||
func (g Service) AssignRoleToUser(c context.Context, req *proto.AssignRoleToUserRequest, res *proto.AssignRoleToUserResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(c, req.AccountUuid)
|
||||
if validationError := validateAssignRoleToUser(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.WriteRoleAssignment(req.AccountUuid, req.RoleId)
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Assignment = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRoleFromUser implements the RoleServiceHandler interface
|
||||
func (g Service) RemoveRoleFromUser(c context.Context, req *proto.RemoveRoleFromUserRequest, _ *empty.Empty) error {
|
||||
if validationError := validateRemoveRoleFromUser(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
if err := g.manager.RemoveRoleAssignment(req.Id); err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPermissionsByResource implements the PermissionServiceHandler interface
|
||||
func (g Service) ListPermissionsByResource(c context.Context, req *proto.ListPermissionsByResourceRequest, res *proto.ListPermissionsByResourceResponse) error {
|
||||
if validationError := validateListPermissionsByResource(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
permissions, err := g.manager.ListPermissionsByResource(req.Resource, g.getRoleIDs(c))
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Permissions = permissions
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPermissionByID implements the PermissionServiceHandler interface
|
||||
func (g Service) GetPermissionByID(c context.Context, req *proto.GetPermissionByIDRequest, res *proto.GetPermissionByIDResponse) error {
|
||||
if validationError := validateGetPermissionByID(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
permission, err := g.manager.ReadPermissionByID(req.PermissionId, g.getRoleIDs(c))
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
if permission == nil {
|
||||
return merrors.NotFound(g.id, "%s", fmt.Errorf("permission %s not found in roles", req.PermissionId))
|
||||
}
|
||||
res.Permission = permission
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanUpResource makes sure that the account uuid of the authenticated user is injected if needed.
|
||||
func cleanUpResource(c context.Context, resource *proto.Resource) {
|
||||
if resource != nil && resource.Type == proto.Resource_TYPE_USER {
|
||||
resource.Id = getValidatedAccountUUID(c, resource.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// getValidatedAccountUUID converts `me` into an actual account uuid from the context, if possible.
|
||||
// the result of this function will always be a valid lower-case UUID or an empty string.
|
||||
func getValidatedAccountUUID(c context.Context, accountUUID string) string {
|
||||
if accountUUID == "me" {
|
||||
if ownAccountUUID, ok := metadata.Get(c, middleware.AccountID); ok {
|
||||
accountUUID = ownAccountUUID
|
||||
}
|
||||
}
|
||||
if accountUUID == "me" {
|
||||
// no matter what happens above, an accountUUID of `me` must not be passed on. Clear it instead.
|
||||
accountUUID = ""
|
||||
}
|
||||
return accountUUID
|
||||
}
|
||||
|
||||
// getRoleIDs extracts the roleIDs of the authenticated user from the context.
|
||||
func (g Service) getRoleIDs(c context.Context) []string {
|
||||
if ownRoleIDs, ok := roles.ReadRoleIDsFromContext(c); ok {
|
||||
return ownRoleIDs
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (g Service) getValueWithIdentifier(value *proto.Value) (*proto.ValueWithIdentifier, error) {
|
||||
bundle, err := g.manager.ReadBundle(value.BundleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setting, err := g.manager.ReadSetting(value.SettingId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proto.ValueWithIdentifier{
|
||||
Identifier: &proto.Identifier{
|
||||
Extension: bundle.Extension,
|
||||
Bundle: bundle.Name,
|
||||
Setting: setting.Name,
|
||||
},
|
||||
Value: value,
|
||||
}, nil
|
||||
}
|
||||
61
settings/pkg/service/v0/service_test.go
Normal file
61
settings/pkg/service/v0/service_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/v2/metadata"
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
ctxWithUUID = metadata.Set(context.Background(), middleware.AccountID, "61445573-4dbe-4d56-88dc-88ab47aceba7")
|
||||
ctxWithEmptyUUID = metadata.Set(context.Background(), middleware.AccountID, "")
|
||||
emptyCtx = context.Background()
|
||||
|
||||
scenarios = []struct {
|
||||
name string
|
||||
accountUUID string
|
||||
ctx context.Context
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "context with UUID; identifier = 'me'",
|
||||
ctx: ctxWithUUID,
|
||||
accountUUID: "me",
|
||||
expect: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
},
|
||||
{
|
||||
name: "context with empty UUID; identifier = 'me'",
|
||||
ctx: ctxWithEmptyUUID,
|
||||
accountUUID: "me",
|
||||
expect: "",
|
||||
},
|
||||
{
|
||||
name: "context without UUID; identifier = 'me'",
|
||||
ctx: emptyCtx,
|
||||
accountUUID: "me",
|
||||
expect: "",
|
||||
},
|
||||
{
|
||||
name: "context with UUID; identifier not 'me'",
|
||||
ctx: ctxWithUUID,
|
||||
accountUUID: "",
|
||||
expect: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetValidatedAccountUUID(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
scenario := s
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
got := getValidatedAccountUUID(scenario.ctx, scenario.accountUUID)
|
||||
assert.NotPanics(t, func() {
|
||||
getValidatedAccountUUID(emptyCtx, scenario.accountUUID)
|
||||
})
|
||||
assert.Equal(t, scenario.expect, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
65
settings/pkg/service/v0/settings.go
Normal file
65
settings/pkg/service/v0/settings.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package svc
|
||||
|
||||
import settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
|
||||
const (
|
||||
// BundleUUIDRoleAdmin represents the admin role
|
||||
BundleUUIDRoleAdmin = "71881883-1768-46bd-a24d-a356a2afdf7f"
|
||||
|
||||
// BundleUUIDRoleUser represents the user role.
|
||||
BundleUUIDRoleUser = "d7beeea8-8ff4-406b-8fb6-ab2dd81e6b11"
|
||||
|
||||
// BundleUUIDRoleGuest represents the guest role.
|
||||
BundleUUIDRoleGuest = "38071a68-456a-4553-846a-fa67bf5596cc"
|
||||
)
|
||||
|
||||
// generateBundlesDefaultRoles bootstraps the default roles.
|
||||
func generateBundlesDefaultRoles() []*settings.Bundle {
|
||||
return []*settings.Bundle{
|
||||
generateBundleAdminRole(),
|
||||
generateBundleUserRole(),
|
||||
generateBundleGuestRole(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateBundleAdminRole() *settings.Bundle {
|
||||
return &settings.Bundle{
|
||||
Id: BundleUUIDRoleAdmin,
|
||||
Name: "admin",
|
||||
Type: settings.Bundle_TYPE_ROLE,
|
||||
Extension: "ocis-roles",
|
||||
DisplayName: "Admin",
|
||||
Resource: &settings.Resource{
|
||||
Type: settings.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Settings: []*settings.Setting{},
|
||||
}
|
||||
}
|
||||
|
||||
func generateBundleUserRole() *settings.Bundle {
|
||||
return &settings.Bundle{
|
||||
Id: BundleUUIDRoleUser,
|
||||
Name: "user",
|
||||
Type: settings.Bundle_TYPE_ROLE,
|
||||
Extension: "ocis-roles",
|
||||
DisplayName: "User",
|
||||
Resource: &settings.Resource{
|
||||
Type: settings.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Settings: []*settings.Setting{},
|
||||
}
|
||||
}
|
||||
|
||||
func generateBundleGuestRole() *settings.Bundle {
|
||||
return &settings.Bundle{
|
||||
Id: BundleUUIDRoleGuest,
|
||||
Name: "guest",
|
||||
Type: settings.Bundle_TYPE_ROLE,
|
||||
Extension: "ocis-roles",
|
||||
DisplayName: "Guest",
|
||||
Resource: &settings.Resource{
|
||||
Type: settings.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Settings: []*settings.Setting{},
|
||||
}
|
||||
}
|
||||
9
settings/pkg/service/v0/tracing.go
Normal file
9
settings/pkg/service/v0/tracing.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package svc
|
||||
|
||||
// NewTracing returns a service that instruments traces.
|
||||
func NewTracing(next Service) Service {
|
||||
return Service{
|
||||
manager: next.manager,
|
||||
config: next.config,
|
||||
}
|
||||
}
|
||||
163
settings/pkg/service/v0/validator.go
Normal file
163
settings/pkg/service/v0/validator.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
var (
|
||||
regexForAccountUUID = regexp.MustCompile(`^[A-Za-z0-9\-_.+@]+$`)
|
||||
requireAccountID = []validation.Rule{
|
||||
// use rule for validation error message consistency (".. must not be blank" on empty strings)
|
||||
validation.Required,
|
||||
validation.Match(regexForAccountUUID),
|
||||
}
|
||||
regexForKeys = regexp.MustCompile(`^[A-Za-z0-9\-_]*$`)
|
||||
requireAlphanumeric = []validation.Rule{
|
||||
validation.Required,
|
||||
validation.Match(regexForKeys),
|
||||
}
|
||||
)
|
||||
|
||||
func validateSaveBundle(req *proto.SaveBundleRequest) error {
|
||||
if err := validation.ValidateStruct(
|
||||
req.Bundle,
|
||||
validation.Field(&req.Bundle.Id, validation.When(req.Bundle.Id != "", is.UUID)),
|
||||
validation.Field(&req.Bundle.Name, requireAlphanumeric...),
|
||||
validation.Field(&req.Bundle.Type, validation.NotIn(proto.Bundle_TYPE_UNKNOWN)),
|
||||
validation.Field(&req.Bundle.Extension, requireAlphanumeric...),
|
||||
validation.Field(&req.Bundle.DisplayName, validation.Required),
|
||||
validation.Field(&req.Bundle.Settings, validation.Required),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateResource(req.Bundle.Resource); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range req.Bundle.Settings {
|
||||
if err := validateSetting(req.Bundle.Settings[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGetBundle(req *proto.GetBundleRequest) error {
|
||||
return validation.Validate(&req.BundleId, is.UUID)
|
||||
}
|
||||
|
||||
func validateListBundles(req *proto.ListBundlesRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAddSettingToBundle(req *proto.AddSettingToBundleRequest) error {
|
||||
if err := validation.ValidateStruct(req, validation.Field(&req.BundleId, is.UUID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateSetting(req.Setting)
|
||||
}
|
||||
|
||||
func validateRemoveSettingFromBundle(req *proto.RemoveSettingFromBundleRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.BundleId, is.UUID),
|
||||
validation.Field(&req.SettingId, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateSaveValue(req *proto.SaveValueRequest) error {
|
||||
if err := validation.ValidateStruct(
|
||||
req.Value,
|
||||
validation.Field(&req.Value.Id, validation.When(req.Value.Id != "", is.UUID)),
|
||||
validation.Field(&req.Value.BundleId, is.UUID),
|
||||
validation.Field(&req.Value.SettingId, is.UUID),
|
||||
validation.Field(&req.Value.AccountUuid, requireAccountID...),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateResource(req.Value.Resource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: validate values against the respective setting. need to check if constraints of the setting are fulfilled.
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGetValue(req *proto.GetValueRequest) error {
|
||||
return validation.Validate(req.Id, is.UUID)
|
||||
}
|
||||
|
||||
func validateGetValueByUniqueIdentifiers(req *proto.GetValueByUniqueIdentifiersRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.SettingId, is.UUID),
|
||||
validation.Field(&req.AccountUuid, requireAccountID...),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListValues(req *proto.ListValuesRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.BundleId, validation.When(req.BundleId != "", is.UUID)),
|
||||
validation.Field(&req.AccountUuid, validation.When(req.AccountUuid != "", validation.Match(regexForAccountUUID))),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListRoles(req *proto.ListBundlesRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateListRoleAssignments(req *proto.ListRoleAssignmentsRequest) error {
|
||||
return validation.Validate(req.AccountUuid, requireAccountID...)
|
||||
}
|
||||
|
||||
func validateAssignRoleToUser(req *proto.AssignRoleToUserRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.AccountUuid, requireAccountID...),
|
||||
validation.Field(&req.RoleId, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateRemoveRoleFromUser(req *proto.RemoveRoleFromUserRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.Id, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListPermissionsByResource(req *proto.ListPermissionsByResourceRequest) error {
|
||||
return validateResource(req.Resource)
|
||||
}
|
||||
|
||||
func validateGetPermissionByID(req *proto.GetPermissionByIDRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.PermissionId, requireAlphanumeric...),
|
||||
)
|
||||
}
|
||||
|
||||
// validateResource is an internal helper for validating the content of a resource.
|
||||
func validateResource(resource *proto.Resource) error {
|
||||
if err := validation.Validate(&resource, validation.Required); err != nil {
|
||||
return err
|
||||
}
|
||||
return validation.Validate(&resource, validation.NotIn(proto.Resource_TYPE_UNKNOWN))
|
||||
}
|
||||
|
||||
// validateSetting is an internal helper for validating the content of a setting.
|
||||
func validateSetting(setting *proto.Setting) error {
|
||||
// TODO: make sanity checks, like for int settings, min <= default <= max.
|
||||
if err := validation.ValidateStruct(
|
||||
setting,
|
||||
validation.Field(&setting.Id, validation.When(setting.Id != "", is.UUID)),
|
||||
validation.Field(&setting.Name, requireAlphanumeric...),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateResource(setting.Resource)
|
||||
}
|
||||
53
settings/pkg/settings/settings.go
Normal file
53
settings/pkg/settings/settings.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
var (
|
||||
// Registry uses the strategy pattern as a registry
|
||||
Registry = map[string]RegisterFunc{}
|
||||
)
|
||||
|
||||
// RegisterFunc stores store constructors
|
||||
type RegisterFunc func(*config.Config) Manager
|
||||
|
||||
// Manager combines service interfaces for abstraction of storage implementations
|
||||
type Manager interface {
|
||||
BundleManager
|
||||
ValueManager
|
||||
RoleAssignmentManager
|
||||
PermissionManager
|
||||
}
|
||||
|
||||
// BundleManager is a bundle service interface for abstraction of storage implementations
|
||||
type BundleManager interface {
|
||||
ListBundles(bundleType proto.Bundle_Type, bundleIDs []string) ([]*proto.Bundle, error)
|
||||
ReadBundle(bundleID string) (*proto.Bundle, error)
|
||||
WriteBundle(bundle *proto.Bundle) (*proto.Bundle, error)
|
||||
ReadSetting(settingID string) (*proto.Setting, error)
|
||||
AddSettingToBundle(bundleID string, setting *proto.Setting) (*proto.Setting, error)
|
||||
RemoveSettingFromBundle(bundleID, settingID string) error
|
||||
}
|
||||
|
||||
// ValueManager is a value service interface for abstraction of storage implementations
|
||||
type ValueManager interface {
|
||||
ListValues(bundleID, accountUUID string) ([]*proto.Value, error)
|
||||
ReadValue(valueID string) (*proto.Value, error)
|
||||
ReadValueByUniqueIdentifiers(accountUUID, settingID string) (*proto.Value, error)
|
||||
WriteValue(value *proto.Value) (*proto.Value, error)
|
||||
}
|
||||
|
||||
// RoleAssignmentManager is a role assignment service interface for abstraction of storage implementations
|
||||
type RoleAssignmentManager interface {
|
||||
ListRoleAssignments(accountUUID string) ([]*proto.UserRoleAssignment, error)
|
||||
WriteRoleAssignment(accountUUID, roleID string) (*proto.UserRoleAssignment, error)
|
||||
RemoveRoleAssignment(assignmentID string) error
|
||||
}
|
||||
|
||||
// PermissionManager is a permissions service interface for abstraction of storage implementations
|
||||
type PermissionManager interface {
|
||||
ListPermissionsByResource(resource *proto.Resource, roleIDs []string) ([]*proto.Permission, error)
|
||||
ReadPermissionByID(permissionID string, roleIDs []string) (*proto.Permission, error)
|
||||
}
|
||||
67
settings/pkg/store/filesystem/assignments.go
Normal file
67
settings/pkg/store/filesystem/assignments.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Package store implements the go-micro store interface
|
||||
package store
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
|
||||
func (s Store) ListRoleAssignments(accountUUID string) ([]*proto.UserRoleAssignment, error) {
|
||||
var records []*proto.UserRoleAssignment
|
||||
assignmentsFolder := s.buildFolderPathForRoleAssignments(false)
|
||||
assignmentFiles, err := ioutil.ReadDir(assignmentsFolder)
|
||||
if err != nil {
|
||||
return records, nil
|
||||
}
|
||||
|
||||
for _, assignmentFile := range assignmentFiles {
|
||||
record := proto.UserRoleAssignment{}
|
||||
err = s.parseRecordFromFile(&record, filepath.Join(assignmentsFolder, assignmentFile.Name()))
|
||||
if err == nil {
|
||||
if record.AccountUuid == accountUUID {
|
||||
records = append(records, &record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// WriteRoleAssignment appends the given role assignment to the existing assignments of the respective account.
|
||||
func (s Store) WriteRoleAssignment(accountUUID, roleID string) (*proto.UserRoleAssignment, error) {
|
||||
// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
|
||||
list, err := s.ListRoleAssignments(accountUUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
filePath := s.buildFilePathForRoleAssignment(list[0].Id, true)
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
assignment := &proto.UserRoleAssignment{
|
||||
Id: uuid.Must(uuid.NewV4()).String(),
|
||||
AccountUuid: accountUUID,
|
||||
RoleId: roleID,
|
||||
}
|
||||
filePath := s.buildFilePathForRoleAssignment(assignment.Id, true)
|
||||
if err := s.writeRecordToFile(assignment, filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
|
||||
return assignment, nil
|
||||
}
|
||||
|
||||
// RemoveRoleAssignment deletes the given role assignment from the existing assignments of the respective account.
|
||||
func (s Store) RemoveRoleAssignment(assignmentID string) error {
|
||||
filePath := s.buildFilePathForRoleAssignment(assignmentID, false)
|
||||
return os.Remove(filePath)
|
||||
}
|
||||
178
settings/pkg/store/filesystem/assignments_test.go
Normal file
178
settings/pkg/store/filesystem/assignments_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
olog "github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
einstein = "a4d07560-a670-4be9-8d60-9b547751a208"
|
||||
//marie = "3c054db3-eec1-4ca4-b985-bc56dcf560cb"
|
||||
|
||||
s = Store{
|
||||
dataPath: dataRoot,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
logger = olog.NewLogger(
|
||||
olog.Color(true),
|
||||
olog.Pretty(true),
|
||||
olog.Level("info"),
|
||||
)
|
||||
|
||||
bundles = []*proto.Bundle{
|
||||
{
|
||||
Id: "f36db5e6-a03c-40df-8413-711c67e40b47",
|
||||
Type: proto.Bundle_TYPE_ROLE,
|
||||
DisplayName: "test role - reads | update",
|
||||
Name: "TEST_ROLE",
|
||||
Extension: "ocis-settings",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_BUNDLE,
|
||||
},
|
||||
Settings: []*proto.Setting{
|
||||
{
|
||||
Name: "update",
|
||||
Value: &proto.Setting_PermissionValue{
|
||||
PermissionValue: &proto.Permission{
|
||||
Operation: proto.Permission_OPERATION_UPDATE,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "read",
|
||||
Value: &proto.Setting_PermissionValue{
|
||||
PermissionValue: &proto.Permission{
|
||||
Operation: proto.Permission_OPERATION_READ,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
|
||||
Type: proto.Bundle_TYPE_ROLE,
|
||||
DisplayName: "another",
|
||||
Name: "ANOTHER_TEST_ROLE",
|
||||
Extension: "ocis-settings",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_BUNDLE,
|
||||
},
|
||||
Settings: []*proto.Setting{
|
||||
{
|
||||
Name: "read",
|
||||
Value: &proto.Setting_PermissionValue{
|
||||
PermissionValue: &proto.Permission{
|
||||
Operation: proto.Permission_OPERATION_READ,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
setupRoles()
|
||||
}
|
||||
|
||||
func setupRoles() {
|
||||
for i := range bundles {
|
||||
if _, err := s.WriteBundle(bundles[i]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssignmentUniqueness(t *testing.T) {
|
||||
var scenarios = []struct {
|
||||
name string
|
||||
userID string
|
||||
firstRole string
|
||||
secondRole string
|
||||
}{
|
||||
{
|
||||
"roles assignments",
|
||||
einstein,
|
||||
"f36db5e6-a03c-40df-8413-711c67e40b47",
|
||||
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario := scenario
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
firstAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, firstAssignment.RoleId, scenario.firstRole)
|
||||
assert.FileExists(t, filepath.Join(dataRoot, "assignments", firstAssignment.Id+".json"))
|
||||
|
||||
list, err := s.ListRoleAssignments(scenario.userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(list))
|
||||
|
||||
// creating another assignment shouldn't add another entry, as we support max one role per user.
|
||||
secondAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.secondRole)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(list))
|
||||
|
||||
// assigning the second role should remove the old file and create a new one.
|
||||
list, err = s.ListRoleAssignments(scenario.userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(list))
|
||||
assert.Equal(t, secondAssignment.RoleId, scenario.secondRole)
|
||||
assert.NoFileExists(t, filepath.Join(dataRoot, "assignments", firstAssignment.Id+".json"))
|
||||
assert.FileExists(t, filepath.Join(dataRoot, "assignments", secondAssignment.Id+".json"))
|
||||
})
|
||||
}
|
||||
burnRoot()
|
||||
}
|
||||
|
||||
func TestDeleteAssignment(t *testing.T) {
|
||||
var scenarios = []struct {
|
||||
name string
|
||||
userID string
|
||||
firstRole string
|
||||
secondRole string
|
||||
}{
|
||||
{
|
||||
"roles assignments",
|
||||
einstein,
|
||||
"f36db5e6-a03c-40df-8413-711c67e40b47",
|
||||
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario := scenario
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
assignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, assignment.RoleId, scenario.firstRole)
|
||||
assert.FileExists(t, filepath.Join(dataRoot, "assignments", assignment.Id+".json"))
|
||||
|
||||
list, err := s.ListRoleAssignments(scenario.userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(list))
|
||||
|
||||
err = s.RemoveRoleAssignment(assignment.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
list, err = s.ListRoleAssignments(scenario.userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(list))
|
||||
|
||||
err = s.RemoveRoleAssignment(assignment.Id)
|
||||
merr := &os.PathError{}
|
||||
assert.Equal(t, true, errors.As(err, &merr))
|
||||
})
|
||||
}
|
||||
burnRoot()
|
||||
}
|
||||
174
settings/pkg/store/filesystem/bundles.go
Normal file
174
settings/pkg/store/filesystem/bundles.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Package store implements the go-micro store interface
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
var m = &sync.RWMutex{}
|
||||
|
||||
// ListBundles returns all bundles in the dataPath folder that match the given type.
|
||||
func (s Store) ListBundles(bundleType proto.Bundle_Type, bundleIDs []string) ([]*proto.Bundle, error) {
|
||||
// FIXME: list requests should be ran against a cache, not FS
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
bundlesFolder := s.buildFolderPathForBundles(false)
|
||||
bundleFiles, err := ioutil.ReadDir(bundlesFolder)
|
||||
if err != nil {
|
||||
return []*proto.Bundle{}, nil
|
||||
}
|
||||
|
||||
records := make([]*proto.Bundle, 0, len(bundleFiles))
|
||||
for _, bundleFile := range bundleFiles {
|
||||
record := proto.Bundle{}
|
||||
err = s.parseRecordFromFile(&record, filepath.Join(bundlesFolder, bundleFile.Name()))
|
||||
if err != nil {
|
||||
s.Logger.Warn().Msgf("error reading %v", bundleFile)
|
||||
continue
|
||||
}
|
||||
if record.Type != bundleType {
|
||||
continue
|
||||
}
|
||||
if len(bundleIDs) > 0 && !containsStr(record.Id, bundleIDs) {
|
||||
continue
|
||||
}
|
||||
records = append(records, &record)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// containsStr checks if the strs slice contains str
|
||||
func containsStr(str string, strs []string) bool {
|
||||
for _, s := range strs {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadBundle tries to find a bundle by the given id within the dataPath.
|
||||
func (s Store) ReadBundle(bundleID string) (*proto.Bundle, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
filePath := s.buildFilePathForBundle(bundleID, false)
|
||||
record := proto.Bundle{}
|
||||
if err := s.parseRecordFromFile(&record, filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// ReadSetting tries to find a setting by the given id within the dataPath.
|
||||
func (s Store) ReadSetting(settingID string) (*proto.Setting, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
bundles, err := s.ListBundles(proto.Bundle_TYPE_DEFAULT, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, bundle := range bundles {
|
||||
for _, setting := range bundle.Settings {
|
||||
if setting.Id == settingID {
|
||||
return setting, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not read setting: %v", settingID)
|
||||
}
|
||||
|
||||
// WriteBundle writes the given record into a file within the dataPath.
|
||||
func (s Store) WriteBundle(record *proto.Bundle) (*proto.Bundle, error) {
|
||||
// FIXME: locking should happen on the file here, not globally.
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if record.Id == "" {
|
||||
record.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
filePath := s.buildFilePathForBundle(record.Id, true)
|
||||
if err := s.writeRecordToFile(record, filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// AddSettingToBundle adds the given setting to the bundle with the given bundleID.
|
||||
func (s Store) AddSettingToBundle(bundleID string, setting *proto.Setting) (*proto.Setting, error) {
|
||||
bundle, err := s.ReadBundle(bundleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setting.Id == "" {
|
||||
setting.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
setSetting(bundle, setting)
|
||||
_, err = s.WriteBundle(bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
// RemoveSettingFromBundle removes the setting from the bundle with the given ids.
|
||||
func (s Store) RemoveSettingFromBundle(bundleID string, settingID string) error {
|
||||
bundle, err := s.ReadBundle(bundleID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if ok := removeSetting(bundle, settingID); ok {
|
||||
if _, err := s.WriteBundle(bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// indexOfSetting finds the index of the given setting within the given bundle.
|
||||
// returns -1 if the setting was not found.
|
||||
func indexOfSetting(bundle *proto.Bundle, settingID string) int {
|
||||
for index := range bundle.Settings {
|
||||
s := bundle.Settings[index]
|
||||
if s.Id == settingID {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// setSetting will append or overwrite the given setting within the given bundle
|
||||
func setSetting(bundle *proto.Bundle, setting *proto.Setting) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
index := indexOfSetting(bundle, setting.Id)
|
||||
if index == -1 {
|
||||
bundle.Settings = append(bundle.Settings, setting)
|
||||
} else {
|
||||
bundle.Settings[index] = setting
|
||||
}
|
||||
}
|
||||
|
||||
// removeSetting will remove the given setting from the given bundle
|
||||
func removeSetting(bundle *proto.Bundle, settingID string) bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
index := indexOfSetting(bundle, settingID)
|
||||
if index == -1 {
|
||||
return false
|
||||
}
|
||||
bundle.Settings = append(bundle.Settings[:index], bundle.Settings[index+1:]...)
|
||||
return true
|
||||
}
|
||||
155
settings/pkg/store/filesystem/bundles_test.go
Normal file
155
settings/pkg/store/filesystem/bundles_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
olog "github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var bundleScenarios = []struct {
|
||||
name string
|
||||
bundle *proto.Bundle
|
||||
}{
|
||||
{
|
||||
name: "generic-test-file-resource",
|
||||
bundle: &proto.Bundle{
|
||||
Id: bundle1,
|
||||
Type: proto.Bundle_TYPE_DEFAULT,
|
||||
Extension: extension1,
|
||||
DisplayName: "test1",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_FILE,
|
||||
Id: "beep",
|
||||
},
|
||||
Settings: []*proto.Setting{
|
||||
{
|
||||
Id: setting1,
|
||||
Description: "test-desc-1",
|
||||
DisplayName: "test-displayname-1",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_FILE,
|
||||
Id: "bleep",
|
||||
},
|
||||
Value: &proto.Setting_IntValue{
|
||||
IntValue: &proto.Int{
|
||||
Min: 0,
|
||||
Max: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "generic-test-system-resource",
|
||||
bundle: &proto.Bundle{
|
||||
Id: bundle2,
|
||||
Type: proto.Bundle_TYPE_DEFAULT,
|
||||
Extension: extension2,
|
||||
DisplayName: "test1",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Settings: []*proto.Setting{
|
||||
{
|
||||
Id: setting2,
|
||||
Description: "test-desc-2",
|
||||
DisplayName: "test-displayname-2",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Value: &proto.Setting_IntValue{
|
||||
IntValue: &proto.Int{
|
||||
Min: 0,
|
||||
Max: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "generic-test-role-bundle",
|
||||
bundle: &proto.Bundle{
|
||||
Id: bundle3,
|
||||
Type: proto.Bundle_TYPE_ROLE,
|
||||
Extension: extension1,
|
||||
DisplayName: "Role1",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Settings: []*proto.Setting{
|
||||
{
|
||||
Id: setting3,
|
||||
Description: "test-desc-3",
|
||||
DisplayName: "test-displayname-3",
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SETTING,
|
||||
Id: setting1,
|
||||
},
|
||||
Value: &proto.Setting_PermissionValue{
|
||||
PermissionValue: &proto.Permission{
|
||||
Operation: proto.Permission_OPERATION_READ,
|
||||
Constraint: proto.Permission_CONSTRAINT_OWN,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestBundles(t *testing.T) {
|
||||
s := Store{
|
||||
dataPath: dataRoot,
|
||||
Logger: olog.NewLogger(
|
||||
olog.Color(true),
|
||||
olog.Pretty(true),
|
||||
olog.Level("info"),
|
||||
),
|
||||
}
|
||||
|
||||
// write bundles
|
||||
for i := range bundleScenarios {
|
||||
index := i
|
||||
t.Run(bundleScenarios[index].name, func(t *testing.T) {
|
||||
filePath := s.buildFilePathForBundle(bundleScenarios[index].bundle.Id, true)
|
||||
if err := s.writeRecordToFile(bundleScenarios[index].bundle, filePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.FileExists(t, filePath)
|
||||
})
|
||||
}
|
||||
|
||||
// check that ListBundles only returns bundles with type DEFAULT
|
||||
bundles, err := s.ListBundles(proto.Bundle_TYPE_DEFAULT, []string{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
for i := range bundles {
|
||||
assert.Equal(t, proto.Bundle_TYPE_DEFAULT, bundles[i].Type)
|
||||
}
|
||||
|
||||
// check that ListBundles filtered by an id only returns that bundle
|
||||
filteredBundles, err := s.ListBundles(proto.Bundle_TYPE_DEFAULT, []string{bundle2})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, 1, len(filteredBundles))
|
||||
if len(filteredBundles) == 1 {
|
||||
assert.Equal(t, bundle2, filteredBundles[0].Id)
|
||||
}
|
||||
|
||||
// check that ListRoles only returns bundles with type ROLE
|
||||
roles, err := s.ListBundles(proto.Bundle_TYPE_ROLE, []string{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
for i := range roles {
|
||||
assert.Equal(t, proto.Bundle_TYPE_ROLE, roles[i].Type)
|
||||
}
|
||||
|
||||
burnRoot()
|
||||
}
|
||||
39
settings/pkg/store/filesystem/io.go
Normal file
39
settings/pkg/store/filesystem/io.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// Unmarshal file into record
|
||||
func (s Store) parseRecordFromFile(record proto.Message, filePath string) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
decoder := jsonpb.Unmarshaler{}
|
||||
if err = decoder.Unmarshal(file, record); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal record into file
|
||||
func (s Store) writeRecordToFile(record proto.Message, filePath string) error {
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
encoder := jsonpb.Marshaler{}
|
||||
if err = encoder.Marshal(file, record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
65
settings/pkg/store/filesystem/paths.go
Normal file
65
settings/pkg/store/filesystem/paths.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const folderNameBundles = "bundles"
|
||||
const folderNameValues = "values"
|
||||
const folderNameAssignments = "assignments"
|
||||
|
||||
// buildFolderPathForBundles builds the folder path for storing settings bundles. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFolderPathForBundles(mkdir bool) string {
|
||||
folderPath := filepath.Join(s.dataPath, folderNameBundles)
|
||||
if mkdir {
|
||||
s.ensureFolderExists(folderPath)
|
||||
}
|
||||
return folderPath
|
||||
}
|
||||
|
||||
// buildFilePathForBundle builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFilePathForBundle(bundleID string, mkdir bool) string {
|
||||
extensionFolder := s.buildFolderPathForBundles(mkdir)
|
||||
return filepath.Join(extensionFolder, bundleID+".json")
|
||||
}
|
||||
|
||||
// buildFolderPathForValues builds the folder path for storing settings values. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFolderPathForValues(mkdir bool) string {
|
||||
folderPath := filepath.Join(s.dataPath, folderNameValues)
|
||||
if mkdir {
|
||||
s.ensureFolderExists(folderPath)
|
||||
}
|
||||
return folderPath
|
||||
}
|
||||
|
||||
// buildFilePathForValue builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFilePathForValue(valueID string, mkdir bool) string {
|
||||
extensionFolder := s.buildFolderPathForValues(mkdir)
|
||||
return filepath.Join(extensionFolder, valueID+".json")
|
||||
}
|
||||
|
||||
// buildFolderPathForRoleAssignments builds the folder path for storing role assignments. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFolderPathForRoleAssignments(mkdir bool) string {
|
||||
roleAssignmentsFolder := filepath.Join(s.dataPath, folderNameAssignments)
|
||||
if mkdir {
|
||||
s.ensureFolderExists(roleAssignmentsFolder)
|
||||
}
|
||||
return roleAssignmentsFolder
|
||||
}
|
||||
|
||||
// buildFilePathForRoleAssignment builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
|
||||
func (s Store) buildFilePathForRoleAssignment(assignmentID string, mkdir bool) string {
|
||||
roleAssignmentsFolder := s.buildFolderPathForRoleAssignments(mkdir)
|
||||
return filepath.Join(roleAssignmentsFolder, assignmentID+".json")
|
||||
}
|
||||
|
||||
// ensureFolderExists checks if the given path is an existing folder and creates one if not existing
|
||||
func (s Store) ensureFolderExists(path string) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0700)
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msgf("Error creating folder %v", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
52
settings/pkg/store/filesystem/permissions.go
Normal file
52
settings/pkg/store/filesystem/permissions.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-settings/pkg/util"
|
||||
)
|
||||
|
||||
// ListPermissionsByResource collects all permissions from the provided roleIDs that match the requested resource
|
||||
func (s Store) ListPermissionsByResource(resource *proto.Resource, roleIDs []string) ([]*proto.Permission, error) {
|
||||
records := make([]*proto.Permission, 0)
|
||||
for _, roleID := range roleIDs {
|
||||
role, err := s.ReadBundle(roleID)
|
||||
if err != nil {
|
||||
s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping")
|
||||
continue
|
||||
}
|
||||
records = append(records, extractPermissionsByResource(resource, role)...)
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// ReadPermissionByID finds the permission in the roles, specified by the provided roleIDs
|
||||
func (s Store) ReadPermissionByID(permissionID string, roleIDs []string) (*proto.Permission, error) {
|
||||
for _, roleID := range roleIDs {
|
||||
role, err := s.ReadBundle(roleID)
|
||||
if err != nil {
|
||||
s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping")
|
||||
continue
|
||||
}
|
||||
for _, permission := range role.Settings {
|
||||
if permission.Id == permissionID {
|
||||
if value, ok := permission.Value.(*proto.Setting_PermissionValue); ok {
|
||||
return value.PermissionValue, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// extractPermissionsByResource collects all permissions from the provided role that match the requested resource
|
||||
func extractPermissionsByResource(resource *proto.Resource, role *proto.Bundle) []*proto.Permission {
|
||||
permissions := make([]*proto.Permission, 0)
|
||||
for _, setting := range role.Settings {
|
||||
if value, ok := setting.Value.(*proto.Setting_PermissionValue); ok {
|
||||
if util.IsResourceMatched(setting.Resource, resource) {
|
||||
permissions = append(permissions, value.PermissionValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return permissions
|
||||
}
|
||||
49
settings/pkg/store/filesystem/store.go
Normal file
49
settings/pkg/store/filesystem/store.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package store implements the go-micro store interface
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
olog "github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/config"
|
||||
"github.com/owncloud/ocis-settings/pkg/settings"
|
||||
)
|
||||
|
||||
var (
|
||||
// Name is the default name for the settings store
|
||||
Name = "ocis-settings"
|
||||
managerName = "filesystem"
|
||||
)
|
||||
|
||||
// Store interacts with the filesystem to manage settings information
|
||||
type Store struct {
|
||||
dataPath string
|
||||
Logger olog.Logger
|
||||
}
|
||||
|
||||
// New creates a new store
|
||||
func New(cfg *config.Config) settings.Manager {
|
||||
s := Store{
|
||||
Logger: olog.NewLogger(
|
||||
olog.Color(cfg.Log.Color),
|
||||
olog.Pretty(cfg.Log.Pretty),
|
||||
olog.Level(cfg.Log.Level),
|
||||
),
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cfg.Storage.DataPath); err != nil {
|
||||
s.Logger.Info().Msgf("creating container on %v", cfg.Storage.DataPath)
|
||||
err := os.MkdirAll(cfg.Storage.DataPath, 0700)
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msgf("providing container on %v", cfg.Storage.DataPath)
|
||||
}
|
||||
}
|
||||
|
||||
s.dataPath = cfg.Storage.DataPath
|
||||
return &s
|
||||
}
|
||||
|
||||
func init() {
|
||||
settings.Registry[managerName] = New
|
||||
}
|
||||
39
settings/pkg/store/filesystem/store_test.go
Normal file
39
settings/pkg/store/filesystem/store_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
// account UUIDs
|
||||
accountUUID1 = "c4572da7-6142-4383-8fc6-efde3d463036"
|
||||
//accountUUID2 = "e11f9769-416a-427d-9441-41a0e51391d7"
|
||||
//accountUUID3 = "633ecd77-1980-412a-8721-bf598a330bb4"
|
||||
|
||||
// extension names
|
||||
extension1 = "test-extension-1"
|
||||
extension2 = "test-extension-2"
|
||||
|
||||
// bundle ids
|
||||
bundle1 = "2f06addf-4fd2-49d5-8f71-00fbd3a3ec47"
|
||||
bundle2 = "2d745744-749c-4286-8e92-74a24d8331c5"
|
||||
bundle3 = "d8fd27d1-c00b-4794-a658-416b756a72ff"
|
||||
|
||||
// setting ids
|
||||
setting1 = "c7ebbc8b-d15a-4f2e-9d7d-d6a4cf858d1a"
|
||||
setting2 = "3fd9a3d9-20b7-40d4-9294-b22bb5868c10"
|
||||
setting3 = "24bb9535-3df4-42f1-a622-7c0562bec99f"
|
||||
|
||||
// value ids
|
||||
value1 = "fd3b6221-dc13-4a22-824d-2480495f1cdb"
|
||||
value2 = "2a0bd9b0-ca1d-491a-8c56-d2ddfd68ded8"
|
||||
//value3 = "b42702d2-5e4d-4d73-b133-e1f9e285355e"
|
||||
|
||||
dataRoot = "/var/tmp/herecomesthesun"
|
||||
)
|
||||
|
||||
func burnRoot() {
|
||||
os.RemoveAll(filepath.Join(dataRoot, "values"))
|
||||
os.RemoveAll(filepath.Join(dataRoot, "bundles"))
|
||||
}
|
||||
109
settings/pkg/store/filesystem/values.go
Normal file
109
settings/pkg/store/filesystem/values.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// Package store implements the go-micro store interface
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// ListValues reads all values that match the given bundleId and accountUUID.
|
||||
// If the bundleId is empty, it's ignored for filtering.
|
||||
// If the accountUUID is empty, only values with empty accountUUID are returned.
|
||||
// If the accountUUID is not empty, values with an empty or with a matching accountUUID are returned.
|
||||
func (s Store) ListValues(bundleID, accountUUID string) ([]*proto.Value, error) {
|
||||
valuesFolder := s.buildFolderPathForValues(false)
|
||||
valueFiles, err := ioutil.ReadDir(valuesFolder)
|
||||
if err != nil {
|
||||
return []*proto.Value{}, nil
|
||||
}
|
||||
|
||||
records := make([]*proto.Value, 0, len(valueFiles))
|
||||
for _, valueFile := range valueFiles {
|
||||
record := proto.Value{}
|
||||
err := s.parseRecordFromFile(&record, filepath.Join(valuesFolder, valueFile.Name()))
|
||||
if err != nil {
|
||||
s.Logger.Warn().Msgf("error reading %v", valueFile)
|
||||
continue
|
||||
}
|
||||
if bundleID != "" && record.BundleId != bundleID {
|
||||
continue
|
||||
}
|
||||
// if requested accountUUID empty -> fetch all system level values
|
||||
if accountUUID == "" && record.AccountUuid != "" {
|
||||
continue
|
||||
}
|
||||
// if requested accountUUID empty -> fetch all individual + all system level values
|
||||
if accountUUID != "" && record.AccountUuid != "" && record.AccountUuid != accountUUID {
|
||||
continue
|
||||
}
|
||||
records = append(records, &record)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// ReadValue tries to find a value by the given valueId within the dataPath
|
||||
func (s Store) ReadValue(valueID string) (*proto.Value, error) {
|
||||
filePath := s.buildFilePathForValue(valueID, false)
|
||||
record := proto.Value{}
|
||||
if err := s.parseRecordFromFile(&record, filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// ReadValueByUniqueIdentifiers tries to find a value given a set of unique identifiers
|
||||
func (s Store) ReadValueByUniqueIdentifiers(accountUUID, settingID string) (*proto.Value, error) {
|
||||
valuesFolder := s.buildFolderPathForValues(false)
|
||||
files, err := ioutil.ReadDir(valuesFolder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range files {
|
||||
if !files[i].IsDir() {
|
||||
r := proto.Value{}
|
||||
s.Logger.Debug().Msgf("reading contents from file: %v", filepath.Join(valuesFolder, files[i].Name()))
|
||||
if err := s.parseRecordFromFile(&r, filepath.Join(valuesFolder, files[i].Name())); err != nil {
|
||||
s.Logger.Debug().Msgf("match found: %v", filepath.Join(valuesFolder, files[i].Name()))
|
||||
return &proto.Value{}, nil
|
||||
}
|
||||
|
||||
// if value saved without accountUUID, then it's a global value
|
||||
if r.AccountUuid == "" && r.SettingId == settingID {
|
||||
return &r, nil
|
||||
}
|
||||
// if value saved with accountUUID, then it's a user specific value
|
||||
if r.AccountUuid == accountUUID && r.SettingId == settingID {
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not read value by settingID=%v and accountID=%v", settingID, accountUUID)
|
||||
}
|
||||
|
||||
// WriteValue writes the given value into a file within the dataPath
|
||||
func (s Store) WriteValue(value *proto.Value) (*proto.Value, error) {
|
||||
s.Logger.Debug().Str("value", value.String()).Msg("writing value")
|
||||
if value.Id == "" {
|
||||
value.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
|
||||
// modify value depending on associated resource
|
||||
if value.Resource.Type == proto.Resource_TYPE_SYSTEM {
|
||||
value.AccountUuid = ""
|
||||
}
|
||||
|
||||
// write the value
|
||||
filePath := s.buildFilePathForValue(value.Id, true)
|
||||
if err := s.writeRecordToFile(value, filePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
70
settings/pkg/store/filesystem/values_test.go
Normal file
70
settings/pkg/store/filesystem/values_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
olog "github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var valueScenarios = []struct {
|
||||
name string
|
||||
value *proto.Value
|
||||
}{
|
||||
{
|
||||
name: "generic-test-with-system-resource",
|
||||
value: &proto.Value{
|
||||
Id: value1,
|
||||
BundleId: bundle1,
|
||||
SettingId: setting1,
|
||||
AccountUuid: accountUUID1,
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
Value: &proto.Value_StringValue{
|
||||
StringValue: "lalala",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "generic-test-with-file-resource",
|
||||
value: &proto.Value{
|
||||
Id: value2,
|
||||
BundleId: bundle1,
|
||||
SettingId: setting2,
|
||||
AccountUuid: accountUUID1,
|
||||
Resource: &proto.Resource{
|
||||
Type: proto.Resource_TYPE_FILE,
|
||||
Id: "adfba82d-919a-41c3-9cd1-5a3f83b2bf76",
|
||||
},
|
||||
Value: &proto.Value_StringValue{
|
||||
StringValue: "tralala",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestValues(t *testing.T) {
|
||||
s := Store{
|
||||
dataPath: dataRoot,
|
||||
Logger: olog.NewLogger(
|
||||
olog.Color(true),
|
||||
olog.Pretty(true),
|
||||
olog.Level("info"),
|
||||
),
|
||||
}
|
||||
for i := range valueScenarios {
|
||||
index := i
|
||||
t.Run(valueScenarios[index].name, func(t *testing.T) {
|
||||
|
||||
filePath := s.buildFilePathForValue(valueScenarios[index].value.Id, true)
|
||||
if err := s.writeRecordToFile(valueScenarios[index].value, filePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.FileExists(t, filePath)
|
||||
})
|
||||
}
|
||||
|
||||
burnRoot()
|
||||
}
|
||||
6
settings/pkg/store/registry.go
Normal file
6
settings/pkg/store/registry.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
// init filesystem store
|
||||
_ "github.com/owncloud/ocis-settings/pkg/store/filesystem"
|
||||
)
|
||||
16
settings/pkg/util/resource_helper.go
Normal file
16
settings/pkg/util/resource_helper.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package util
|
||||
|
||||
import "github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
|
||||
const (
|
||||
// ResourceIDAll declares on a resource that it matches any id
|
||||
ResourceIDAll = "all"
|
||||
)
|
||||
|
||||
// IsResourceMatched checks if the `example` resource is an exact match or a subset of `definition`
|
||||
func IsResourceMatched(definition, example *proto.Resource) bool {
|
||||
if definition.Type != example.Type {
|
||||
return false
|
||||
}
|
||||
return definition.Id == ResourceIDAll || definition.Id == example.Id
|
||||
}
|
||||
91
settings/pkg/util/resource_helper_test.go
Normal file
91
settings/pkg/util/resource_helper_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestIsResourceMatched(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
definition *proto.Resource
|
||||
example *proto.Resource
|
||||
matched bool
|
||||
}{
|
||||
{
|
||||
"same resource types without ids match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"different resource types without ids don't match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_SYSTEM,
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"same resource types with different ids don't match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: "einstein",
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: "marie",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"same resource types with same ids match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: "einstein",
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: "einstein",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"same resource types with definition = ALL and without id in example is a match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: ResourceIDAll,
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"same resource types with definition.id = ALL and with some id in example is a match",
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: ResourceIDAll,
|
||||
},
|
||||
&proto.Resource{
|
||||
Type: proto.Resource_TYPE_USER,
|
||||
Id: "einstein",
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
assert.Equal(t, scenario.matched, IsResourceMatched(scenario.definition, scenario.example))
|
||||
})
|
||||
}
|
||||
}
|
||||
19
settings/pkg/version/version.go
Normal file
19
settings/pkg/version/version.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// String gets defined by the build system.
|
||||
String = "0.0.0"
|
||||
|
||||
// Date indicates the build date.
|
||||
Date = "00000000"
|
||||
)
|
||||
|
||||
// Compiled returns the compile time of this service.
|
||||
func Compiled() time.Time {
|
||||
t, _ := time.Parse("20060102", Date)
|
||||
return t
|
||||
}
|
||||
Reference in New Issue
Block a user