Merge branch 'master' into no-additional-init

This commit is contained in:
A.Unger
2021-07-02 13:25:30 +02:00
662 changed files with 43301 additions and 29594 deletions
+10 -142
View File
@@ -1,161 +1,29 @@
SHELL := bash
NAME := ocs
IMPORT := github.com/owncloud/ocis/$(NAME)
BIN := bin
DIST := dist
ifeq ($(OS), Windows_NT)
EXECUTABLE := $(NAME).exe
UNAME := Windows
else
EXECUTABLE := $(NAME)
UNAME := $(shell uname -s)
############ tooling ############
ifneq (, $(shell which go 2> /dev/null)) # supress `command not found warnings` for non go targets in CI
include ../.bingo/Variables.mk
endif
ifeq ($(UNAME), Darwin)
GOBUILD ?= go build -i
else
GOBUILD ?= go build
endif
############ go tooling ############
include ../.make/go.mk
PACKAGES ?= $(shell go list ./...)
SOURCES ?= $(shell find . -name "*.go" -type f -not -path "./node_modules/*")
GENERATE ?= $(PACKAGES)
TAGS ?=
ifndef OUTPUT
ifneq ($(DRONE_TAG),)
OUTPUT ?= $(subst v,,$(DRONE_TAG))
else
OUTPUT ?= testing
endif
endif
ifndef VERSION
ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG))
else
VERSION ?= $(shell git rev-parse --short HEAD)
endif
endif
ifndef DATE
DATE := $(shell date -u '+%Y%m%d')
endif
LDFLAGS += -s -w -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)"
DEBUG_LDFLAGS += -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)"
GCFLAGS += all=-N -l
.PHONY: all
all: build
.PHONY: sync
sync:
go mod download
.PHONY: clean
clean:
go clean -i ./...
rm -rf $(BIN) $(DIST)
.PHONY: go-mod-tidy
go-mod-tidy:
@go mod tidy
.PHONY: fmt
fmt:
gofmt -s -w $(SOURCES)
.PHONY: vet
vet:
go vet $(PACKAGES)
.PHONY: staticcheck
staticcheck:
go run honnef.co/go/tools/cmd/staticcheck -tags '$(TAGS)' $(PACKAGES)
.PHONY: lint
lint:
for PKG in $(PACKAGES); do go run golang.org/x/lint/golint -set_exit_status $$PKG || exit 1; done;
.PHONY: changelog
changelog:
go run github.com/restic/calens >| CHANGELOG.md
.PHONY: test
test:
go run github.com/haya14busa/goverage -v -coverprofile coverage.out $(PACKAGES)
.PHONY: go-coverage
go-coverage:
@if [ ! -f coverage.out ]; then $(MAKE) test &>/dev/null; fi;
@go tool cover -func coverage.out | tail -1 | grep -Eo "[0-9]+\.[0-9]+"
.PHONY: install
install: $(SOURCES)
go install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/$(NAME)
.PHONY: build
build: $(BIN)/$(EXECUTABLE) $(BIN)/$(EXECUTABLE)-debug
$(BIN)/$(EXECUTABLE): $(SOURCES)
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ ./cmd/$(NAME)
$(BIN)/$(EXECUTABLE)-debug: $(SOURCES)
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(DEBUG_LDFLAGS)' -gcflags '$(GCFLAGS)' -o $@ ./cmd/$(NAME)
.PHONY: release
release: release-dirs release-linux release-windows release-darwin release-copy release-check
.PHONY: release-dirs
release-dirs:
mkdir -p $(DIST)/binaries $(DIST)/release
.PHONY: release-linux
release-linux: release-dirs
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
.PHONY: release-windows
release-windows: release-dirs
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
.PHONY: release-darwin
release-darwin: release-dirs
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
.PHONY: release-copy
release-copy:
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
.PHONY: release-check
release-check:
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
.PHONY: release-finish
release-finish: release-copy release-check
.PHONY: watch
watch:
go run github.com/cespare/reflex -c reflex.conf
############ release ############
include ../.make/release.mk
############ docs generate ############
include ../.make/docs.mk
.PHONY: docs-generate
docs-generate: config-docs-generate
.PHONY: config-docs-generate
config-docs-generate:
go run github.com/owncloud/flaex >| ../docs/extensions/$(NAME)/configuration.md
############ generate ############
.PHONY: generate
generate: ci-node-generate ci-go-generate
include ../.make/generate.mk
.PHONY: ci-go-generate
ci-go-generate: # CI runs ci-node-generate automatically before this target
go generate $(GENERATE)
@go generate $(GENERATE)
.PHONY: ci-node-generate
ci-node-generate:
+2 -1
View File
@@ -4,10 +4,11 @@ import (
"os"
"github.com/owncloud/ocis/ocs/pkg/command"
"github.com/owncloud/ocis/ocs/pkg/config"
)
func main() {
if err := command.Execute(); err != nil {
if err := command.Execute(config.New()); err != nil {
os.Exit(1)
}
}
+19 -26
View File
@@ -1,52 +1,45 @@
module github.com/owncloud/ocis/ocs
go 1.15
go 1.16
require (
contrib.go.opencensus.io/exporter/jaeger v0.2.1
contrib.go.opencensus.io/exporter/ocagent v0.7.0
contrib.go.opencensus.io/exporter/zipkin v0.1.1
github.com/UnnoTed/fileb0x v1.1.4
github.com/asim/go-micro/plugins/client/grpc/v3 v3.0.0-20210217182006-0f0ace1a44a9
contrib.go.opencensus.io/exporter/zipkin v0.1.2
github.com/asim/go-micro/plugins/client/grpc/v3 v3.0.0-20210408173139-0d57213d3f5c
github.com/asim/go-micro/v3 v3.5.1-0.20210217182006-0f0ace1a44a9
github.com/bmatcuk/doublestar v1.3.4 // indirect
github.com/cs3org/go-cs3apis v0.0.0-20210209082852-35ace33082f5
github.com/cs3org/reva v1.6.0
github.com/cs3org/go-cs3apis v0.0.0-20210614143420-5ee2eb1e7887
github.com/cs3org/reva v1.9.1-0.20210628143859-9d29c36c0c3f
github.com/go-chi/chi v4.1.2+incompatible
github.com/go-chi/render v1.0.1
github.com/golang/protobuf v1.4.3
github.com/karrick/godirwalk v1.16.1 // indirect
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/golang/protobuf v1.5.2
github.com/micro/cli/v2 v2.1.2
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/nsf/termbox-go v0.0.0-20210114135735-d04385b850e8 // indirect
github.com/oklog/run v1.1.0
github.com/olekukonko/tablewriter v0.0.4
github.com/openzipkin/zipkin-go v0.2.2
github.com/olekukonko/tablewriter v0.0.5
github.com/openzipkin/zipkin-go v0.2.5
github.com/owncloud/ocis/accounts v0.5.3-0.20210216094451-dc73176dc62d
github.com/owncloud/ocis/ocis-pkg v0.0.0-20210216094451-dc73176dc62d
github.com/owncloud/ocis/settings v0.0.0-20210216094451-dc73176dc62d
github.com/owncloud/ocis/store v0.0.0-20210216094451-dc73176dc62d
github.com/prometheus/client_golang v1.7.1
github.com/restic/calens v0.2.0
github.com/spf13/viper v1.7.0
github.com/owncloud/ocis/proxy v0.0.0-20210412105747-9b95e9b1191b
github.com/owncloud/ocis/settings v0.0.0-20210413063522-955bd60edf33
github.com/owncloud/ocis/store v0.0.0-20210413063522-955bd60edf33
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.10.0
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.7.0
github.com/valyala/fasttemplate v1.2.1 // indirect
go.opencensus.io v0.22.6
github.com/thejerf/suture/v4 v4.0.0
go.opencensus.io v0.23.0
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea
google.golang.org/protobuf v1.25.0
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.27.0
)
replace (
github.com/owncloud/ocis/accounts => ../accounts
github.com/owncloud/ocis/ocis-pkg => ../ocis-pkg
github.com/owncloud/ocis/proxy => ../proxy
github.com/owncloud/ocis/settings => ../settings
github.com/owncloud/ocis/store => ../store
// taken from https://github.com/asim/go-micro/blob/master/plugins/registry/etcd/go.mod#L14-L16
go.etcd.io/etcd/api/v3 => go.etcd.io/etcd/api/v3 v3.0.0-20210204162551-dae29bb719dd
go.etcd.io/etcd/pkg/v3 => go.etcd.io/etcd/pkg/v3 v3.0.0-20210204162551-dae29bb719dd
// latest version compatible with etcd
google.golang.org/grpc => google.golang.org/grpc v1.29.1
)
+524 -211
View File
File diff suppressed because it is too large Load Diff
+34 -6
View File
@@ -1,21 +1,23 @@
package command
import (
"context"
"os"
"strings"
"github.com/owncloud/ocis/ocis-pkg/sync"
"github.com/micro/cli/v2"
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocs/pkg/config"
"github.com/owncloud/ocis/ocs/pkg/flagset"
"github.com/owncloud/ocis/ocs/pkg/version"
"github.com/spf13/viper"
"github.com/thejerf/suture/v4"
)
// Execute is the entry point for the ocis-ocs command.
func Execute() error {
cfg := config.New()
func Execute(cfg *config.Config) error {
app := &cli.App{
Name: "ocis-ocs",
Version: version.String,
@@ -29,8 +31,6 @@ func Execute() error {
},
},
Flags: flagset.RootWithConfig(cfg),
Before: func(c *cli.Context) error {
cfg.Service.Version = version.String
return nil
@@ -63,11 +63,14 @@ func NewLogger(cfg *config.Config) log.Logger {
log.Level(cfg.Log.Level),
log.Pretty(cfg.Log.Pretty),
log.Color(cfg.Log.Color),
log.File(cfg.Log.File),
)
}
// ParseConfig reads ocs configuration from fs.
func ParseConfig(c *cli.Context, cfg *config.Config) error {
sync.ParsingViperConfig.Lock()
defer sync.ParsingViperConfig.Unlock()
logger := NewLogger(cfg)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
@@ -108,3 +111,28 @@ func ParseConfig(c *cli.Context, cfg *config.Config) error {
return nil
}
// SutureService allows for the ocs command to be embedded and supervised by a suture supervisor tree.
type SutureService struct {
cfg *config.Config
}
// NewSutureService creates a new ocs.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
if cfg.Mode == 0 {
cfg.OCS.Supervised = true
}
cfg.OCS.Log.File = cfg.Log.File
return SutureService{
cfg: cfg.OCS,
}
}
func (s SutureService) Serve(ctx context.Context) error {
s.cfg.Context = ctx
if err := Execute(s.cfg); err != nil {
return err
}
return nil
}
+26 -140
View File
@@ -2,25 +2,19 @@ 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/owncloud/ocis/ocs/pkg/tracing"
"github.com/owncloud/ocis/ocis-pkg/sync"
"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/ocs/pkg/config"
"github.com/owncloud/ocis/ocs/pkg/flagset"
"github.com/owncloud/ocis/ocs/pkg/metrics"
"github.com/owncloud/ocis/ocs/pkg/server/debug"
"github.com/owncloud/ocis/ocs/pkg/server/http"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
// Server is the entrypoint for the server command.
@@ -29,110 +23,34 @@ func Server(cfg *config.Config) *cli.Command {
Name: "server",
Usage: "Start integrated server",
Flags: flagset.ServerWithConfig(cfg),
Before: func(c *cli.Context) error {
Before: func(ctx *cli.Context) error {
logger := NewLogger(cfg)
if cfg.HTTP.Root != "/" {
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
}
return ParseConfig(c, cfg)
if !cfg.Supervised {
return ParseConfig(ctx, cfg)
}
logger.Debug().Str("service", "ocs").Msg("ignoring config file parsing when running supervised")
return nil
},
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,
Process: jaeger.Process{
ServiceName: cfg.Tracing.Service,
},
},
)
logger.Info().
Str("collector", cfg.Tracing.Collector).
Msg("Trace collector added")
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")
if err := tracing.Configure(cfg, logger); err != nil {
return err
}
var (
gr = run.Group{}
ctx, cancel = context.WithCancel(context.Background())
metrics = metrics.New()
ctx, cancel = func() (context.Context, context.CancelFunc) {
if cfg.Context == nil {
return context.WithCancel(context.Background())
}
return context.WithCancel(cfg.Context)
}()
metrics = metrics.New()
)
defer cancel()
@@ -145,8 +63,6 @@ func Server(cfg *config.Config) *cli.Command {
http.Context(ctx),
http.Config(cfg),
http.Metrics(metrics),
http.Flags(flagset.RootWithConfig(config.New())),
http.Flags(flagset.ServerWithConfig(config.New())),
)
if err != nil {
@@ -177,48 +93,18 @@ func Server(cfg *config.Config) *cli.Command {
)
if err != nil {
logger.Info().
Err(err).
Str("transport", "debug").
Msg("Failed to initialize server")
logger.Info().Err(err).Str("transport", "debug").Msg("Failed to initialize server")
return err
}
gr.Add(func() error {
return server.ListenAndServe()
}, func(_ error) {
ctx, timeout := context.WithTimeout(ctx, 5*time.Second)
defer timeout()
defer cancel()
if err := server.Shutdown(ctx); err != nil {
logger.Info().
Err(err).
Str("transport", "debug").
Msg("Failed to shutdown server")
} else {
logger.Info().
Str("transport", "debug").
Msg("Shutting down server")
}
gr.Add(server.ListenAndServe, func(_ error) {
_ = server.Shutdown(ctx)
cancel()
})
}
{
stop := make(chan os.Signal, 1)
gr.Add(func() error {
signal.Notify(stop, os.Interrupt)
<-stop
return nil
}, func(err error) {
close(stop)
cancel()
})
if !cfg.Supervised {
sync.Trap(&gr, cancel)
}
return gr.Run()
+24 -7
View File
@@ -1,10 +1,13 @@
package config
import "context"
// Log defines the available logging configuration.
type Log struct {
Level string
Pretty bool
Color bool
File string
}
// Debug defines the available debug configuration.
@@ -42,15 +45,29 @@ type TokenManager struct {
JWTSecret string
}
// IdentityManagement keeps track of the OIDC address. This is because Reva requisite of uniqueness for users
// is based in the combination of IDP hostname + UserID. For more information see:
// https://github.com/cs3org/reva/blob/4fd0229f13fae5bc9684556a82dbbd0eced65ef9/pkg/storage/utils/decomposedfs/node/node.go#L856-L865
type IdentityManagement struct {
Address string
}
// Config combines all available configuration parts.
type Config struct {
File string
Log Log
Debug Debug
HTTP HTTP
Tracing Tracing
TokenManager TokenManager
Service Service
File string
Log Log
Debug Debug
HTTP HTTP
Tracing Tracing
TokenManager TokenManager
Service Service
AccountBackend string
RevaAddress string
StorageUsersDriver string
IdentityManagement IdentityManagement
Context context.Context
Supervised bool
}
// New initializes a new configuration with or without defaults.
+73 -42
View File
@@ -2,42 +2,16 @@ package flagset
import (
"github.com/micro/cli/v2"
"github.com/owncloud/ocis/ocis-pkg/flags"
"github.com/owncloud/ocis/ocs/pkg/config"
)
// RootWithConfig applies cfg to the root flagset
func RootWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "log-level",
Value: "info",
Usage: "Set logging level",
EnvVars: []string{"OCS_LOG_LEVEL"},
Destination: &cfg.Log.Level,
},
&cli.BoolFlag{
Name: "log-pretty",
Value: true,
Usage: "Enable pretty logging",
EnvVars: []string{"OCS_LOG_PRETTY"},
Destination: &cfg.Log.Pretty,
},
&cli.BoolFlag{
Name: "log-color",
Value: true,
Usage: "Enable colored logging",
EnvVars: []string{"OCS_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:9114",
Value: flags.OverrideDefaultString(cfg.Debug.Addr, "0.0.0.0:9114"),
Usage: "Address to debug endpoint",
EnvVars: []string{"OCS_DEBUG_ADDR"},
Destination: &cfg.Debug.Addr,
@@ -48,6 +22,30 @@ func HealthWithConfig(cfg *config.Config) []cli.Flag {
// ServerWithConfig applies cfg to the root flagset
func ServerWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "log-file",
Usage: "Enable log to file",
EnvVars: []string{"OCS_LOG_FILE", "OCIS_LOG_FILE"},
Destination: &cfg.Log.File,
},
&cli.StringFlag{
Name: "log-level",
Usage: "Set logging level",
EnvVars: []string{"OCS_LOG_LEVEL", "OCIS_LOG_LEVEL"},
Destination: &cfg.Log.Level,
},
&cli.BoolFlag{
Name: "log-pretty",
Usage: "Enable pretty logging",
EnvVars: []string{"OCS_LOG_PRETTY", "OCIS_LOG_PRETTY"},
Destination: &cfg.Log.Pretty,
},
&cli.BoolFlag{
Name: "log-color",
Usage: "Enable colored logging",
EnvVars: []string{"OCS_LOG_COLOR", "OCIS_LOG_COLOR"},
Destination: &cfg.Log.Color,
},
&cli.StringFlag{
Name: "config-file",
Value: "",
@@ -57,49 +55,49 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
},
&cli.BoolFlag{
Name: "tracing-enabled",
Value: false,
Value: flags.OverrideDefaultBool(cfg.Tracing.Enabled, false),
Usage: "Enable sending traces",
EnvVars: []string{"OCS_TRACING_ENABLED"},
Destination: &cfg.Tracing.Enabled,
},
&cli.StringFlag{
Name: "tracing-type",
Value: "jaeger",
Value: flags.OverrideDefaultString(cfg.Tracing.Type, "jaeger"),
Usage: "Tracing backend type",
EnvVars: []string{"OCS_TRACING_TYPE"},
Destination: &cfg.Tracing.Type,
},
&cli.StringFlag{
Name: "tracing-endpoint",
Value: "",
Value: flags.OverrideDefaultString(cfg.Tracing.Endpoint, ""),
Usage: "Endpoint for the agent",
EnvVars: []string{"OCS_TRACING_ENDPOINT"},
Destination: &cfg.Tracing.Endpoint,
},
&cli.StringFlag{
Name: "tracing-collector",
Value: "",
Value: flags.OverrideDefaultString(cfg.Tracing.Collector, ""),
Usage: "Endpoint for the collector",
EnvVars: []string{"OCS_TRACING_COLLECTOR"},
Destination: &cfg.Tracing.Collector,
},
&cli.StringFlag{
Name: "tracing-service",
Value: "ocs",
Value: flags.OverrideDefaultString(cfg.Tracing.Service, "ocs"),
Usage: "Service name for tracing",
EnvVars: []string{"OCS_TRACING_SERVICE"},
Destination: &cfg.Tracing.Service,
},
&cli.StringFlag{
Name: "debug-addr",
Value: "0.0.0.0:9114",
Value: flags.OverrideDefaultString(cfg.Debug.Addr, "0.0.0.0:9114"),
Usage: "Address to bind debug server",
EnvVars: []string{"OCS_DEBUG_ADDR"},
Destination: &cfg.Debug.Addr,
},
&cli.StringFlag{
Name: "debug-token",
Value: "",
Value: flags.OverrideDefaultString(cfg.Debug.Token, ""),
Usage: "Token to grant metrics access",
EnvVars: []string{"OCS_DEBUG_TOKEN"},
Destination: &cfg.Debug.Token,
@@ -118,28 +116,28 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
},
&cli.StringFlag{
Name: "http-addr",
Value: "0.0.0.0:9110",
Value: flags.OverrideDefaultString(cfg.HTTP.Addr, "0.0.0.0:9110"),
Usage: "Address to bind http server",
EnvVars: []string{"OCS_HTTP_ADDR"},
Destination: &cfg.HTTP.Addr,
},
&cli.StringFlag{
Name: "http-namespace",
Value: "com.owncloud.web",
Value: flags.OverrideDefaultString(cfg.Service.Namespace, "com.owncloud.web"),
Usage: "Set the base namespace for the http namespace",
EnvVars: []string{"OCS_NAMESPACE"},
Destination: &cfg.Service.Namespace,
},
&cli.StringFlag{
Name: "name",
Value: "ocs",
Value: flags.OverrideDefaultString(cfg.Service.Name, "ocs"),
Usage: "Service name",
EnvVars: []string{"OCS_NAME"},
Destination: &cfg.Service.Name,
},
&cli.StringFlag{
Name: "http-root",
Value: "/ocs",
Value: flags.OverrideDefaultString(cfg.HTTP.Root, "/ocs"),
Usage: "Root path of http server",
EnvVars: []string{"OCS_HTTP_ROOT"},
Destination: &cfg.HTTP.Root,
@@ -147,11 +145,44 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
&cli.StringFlag{
Name: "jwt-secret",
Value: "Pive-Fumkiu4",
Value: flags.OverrideDefaultString(cfg.TokenManager.JWTSecret, "Pive-Fumkiu4"),
Usage: "Used to dismantle the access token, should equal reva's jwt-secret",
EnvVars: []string{"OCS_JWT_SECRET", "OCIS_JWT_SECRET"},
Destination: &cfg.TokenManager.JWTSecret,
},
&cli.StringFlag{
Name: "account-backend-type",
Value: flags.OverrideDefaultString(cfg.AccountBackend, "accounts"),
Usage: "account-backend-type",
EnvVars: []string{"OCS_ACCOUNT_BACKEND_TYPE"},
Destination: &cfg.AccountBackend,
},
&cli.StringFlag{
Name: "reva-gateway-addr",
Value: flags.OverrideDefaultString(cfg.RevaAddress, "127.0.0.1:9142"),
Usage: "REVA Gateway Endpoint",
EnvVars: []string{"OCS_REVA_GATEWAY_ADDR"},
Destination: &cfg.RevaAddress,
},
&cli.StringFlag{
Name: "idm-address",
Value: flags.OverrideDefaultString(cfg.IdentityManagement.Address, "https://localhost:9200"),
EnvVars: []string{"OCS_IDM_ADDRESS", "OCIS_URL"},
Usage: "keeps track of the IDM Address. Needed because of Reva requisite of uniqueness for users",
Destination: &cfg.IdentityManagement.Address,
},
&cli.StringFlag{
Name: "users-driver",
Value: flags.OverrideDefaultString(cfg.StorageUsersDriver, "ocis"),
Usage: "storage driver for users mount: eg. local, eos, owncloud, ocis or s3",
EnvVars: []string{"OCS_STORAGE_USERS_DRIVER", "STORAGE_USERS_DRIVER"},
Destination: &cfg.StorageUsersDriver,
},
&cli.StringFlag{
Name: "extensions",
Usage: "Run specific extensions during supervised mode",
},
}
}
@@ -160,14 +191,14 @@ func ListOcsWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "http-namespace",
Value: "com.owncloud.web",
Value: flags.OverrideDefaultString(cfg.Service.Namespace, "com.owncloud.web"),
Usage: "Set the base namespace for the http namespace",
EnvVars: []string{"OCS_NAMESPACE"},
Destination: &cfg.Service.Namespace,
},
&cli.StringFlag{
Name: "name",
Value: "ocs",
Value: flags.OverrideDefaultString(cfg.Service.Name, "ocs"),
Usage: "Service name",
EnvVars: []string{"OCS_NAME"},
Destination: &cfg.Service.Name,
+8 -3
View File
@@ -20,7 +20,7 @@ func RequireAdmin(opts ...Option) func(next http.Handler) http.Handler {
// get roles from context
roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context())
if !ok {
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
return
}
@@ -30,8 +30,13 @@ func RequireAdmin(opts ...Option) func(next http.Handler) http.Handler {
return
}
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
})
}
}
func mustNotFail(err error) {
if err != nil {
panic(err)
}
}
+4 -4
View File
@@ -21,17 +21,17 @@ func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
u, ok := user.ContextGetUser(r.Context())
if !ok {
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
return
}
// get roles from context
roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context())
if !ok {
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
return
}
@@ -50,7 +50,7 @@ func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
}
}
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
})
}
+2 -2
View File
@@ -17,11 +17,11 @@ func RequireUser() func(next http.Handler) http.Handler {
u, ok := user.ContextGetUser(r.Context())
if !ok {
render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
return
}
+6 -2
View File
@@ -33,7 +33,9 @@ func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
if _, err := io.WriteString(w, http.StatusText(http.StatusOK)); err != nil {
panic(err)
}
}
}
@@ -45,6 +47,8 @@ func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
if _, err := io.WriteString(w, http.StatusText(http.StatusOK)); err != nil {
panic(err)
}
}
}
+3 -1
View File
@@ -46,7 +46,9 @@ func Server(opts ...Option) (http.Service, error) {
handle = svc.NewTracing(handle)
}
micro.RegisterHandler(service.Server(), handle)
if err := micro.RegisterHandler(service.Server(), handle); err != nil {
return http.Service{}, err
}
return service, nil
}
+14 -16
View File
@@ -11,13 +11,13 @@ import (
"net/http/httptest"
"net/url"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/asim/go-micro/v3/client"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/auth/scope"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/token/manager/jwt"
"github.com/golang/protobuf/ptypes/empty"
@@ -35,9 +35,8 @@ import (
)
const (
ocsV1 string = "v1.php"
ocsV2 string = "v2.php"
adminBasicAuth string = "admin:admin"
ocsV1 string = "v1.php"
ocsV2 string = "v2.php"
)
const unsuccessfulResponseText string = "The response was expected to be successful but was not"
@@ -604,7 +603,9 @@ func cleanUp(t *testing.T) {
}
if !found {
deleteAccount(t, f.Name())
if _, err := deleteAccount(t, f.Name()); err != nil {
panic(err)
}
}
}
@@ -625,7 +626,9 @@ func cleanUp(t *testing.T) {
}
if !found {
deleteGroup(t, f.Name())
if _, err := deleteGroup(t, f.Name()); err != nil {
panic(err)
}
}
}
}
@@ -639,25 +642,20 @@ func mintToken(ctx context.Context, su *User, roleIds []string) (token string, e
Id: &user.UserId{
OpaqueId: su.ID,
},
Groups: []string{},
Opaque: &types.Opaque{
Map: map[string]*types.OpaqueEntry{
"uid": {
Decoder: "plain",
Value: []byte(strconv.Itoa(su.UIDNumber)),
},
"gid": {
Decoder: "plain",
Value: []byte(strconv.Itoa(su.GIDNumber)),
},
"roles": {
Decoder: "json",
Value: roleIDsJSON,
},
},
},
Groups: []string{},
UidNumber: int64(su.UIDNumber),
GidNumber: int64(su.GIDNumber),
}
return tokenManager.MintToken(ctx, u)
s, _ := scope.GetOwnerScope()
return tokenManager.MintToken(ctx, u, s)
}
func sendRequest(method, endpoint, body string, u *User, roleIds []string) (*httptest.ResponseRecorder, error) {
+2 -2
View File
@@ -10,11 +10,11 @@ import (
// GetConfig renders the ocs config endpoint
func (o Ocs) GetConfig(w http.ResponseWriter, r *http.Request) {
render.Render(w, r, response.DataRender(&data.ConfigData{
mustNotFail(render.Render(w, r, response.DataRender(&data.ConfigData{
Version: "1.7", // TODO get from env
Website: "ocis", // TODO get from env
Host: "", // TODO get from FRONTEND config
Contact: "", // TODO get from env
SSL: "true", // TODO get from env
}))
})))
}
+65 -44
View File
@@ -9,6 +9,8 @@ import (
"regexp"
"strconv"
"github.com/cs3org/reva/pkg/user"
merrors "github.com/asim/go-micro/v3/errors"
"github.com/go-chi/chi"
"github.com/go-chi/render"
@@ -24,6 +26,19 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
var account *accounts.Account
var err error
// short circuit if there is a user already in the context
if u, ok := user.ContextGetUser(r.Context()); ok {
// we are not sure whether the current user in the context is the admin or the authenticated user.
if u.Username == userid {
// the OCS API is a REST API and it uses the username to look for groups. If the id from the user in the context
// differs from that of the url we can assume we are an admin because we are past the selfOrAdmin middleware.
if len(u.Groups) > 0 {
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: u.Groups})))
return
}
}
}
if isValidUUID(userid) {
account, err = o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{
Id: userid,
@@ -34,16 +49,16 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups")
return
}
}
groups := []string{}
groups := make([]string, 0, len(account.MemberOf))
for i := range account.MemberOf {
if account.MemberOf[i].OnPremisesSamAccountName == "" {
o.logger.Warn().Str("groupid", account.MemberOf[i].Id).Msg("group on_premises_sam_account_name is empty, trying to lookup by id")
@@ -67,26 +82,26 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
}
o.logger.Error().Err(err).Int("count", len(groups)).Str("userid", account.Id).Msg("listing groups for user")
render.Render(w, r, response.DataRender(&data.Groups{Groups: groups}))
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: groups})))
}
// AddToGroup adds a user to a group
func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
mustNotFail(r.ParseForm())
userid := chi.URLParam(r, "userid")
groupid := r.PostForm.Get("groupid")
if groupid == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty group assignment: unspecified group"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty group assignment: unspecified group")))
return
}
account, err := o.fetchAccountByUsername(r.Context(), userid)
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
return
}
@@ -96,9 +111,9 @@ func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
return
}
@@ -111,16 +126,16 @@ func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", account.Id).Str("groupid", group.Id).Msg("could not add user to group")
return
}
o.logger.Debug().Str("userid", account.Id).Str("groupid", group.Id).Msg("added user to group")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// RemoveFromGroup removes a user from a group
@@ -134,23 +149,23 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
// read it manually
body, err := ioutil.ReadAll(r.Body)
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error())))
return
}
if err = r.Body.Close(); err != nil {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
return
}
values, err := url.ParseQuery(string(body))
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error())))
return
}
groupid := values.Get("groupid")
if groupid == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "no group id")))
return
}
@@ -166,9 +181,9 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups")
return
@@ -180,9 +195,9 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
return
}
@@ -195,16 +210,16 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", account.Id).Str("groupid", group.Id).Msg("could not remove user from group")
return
}
o.logger.Debug().Str("userid", account.Id).Str("groupid", group.Id).Msg("removed user from group")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// ListGroups lists all groups
@@ -221,16 +236,16 @@ func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
if err != nil {
o.logger.Err(err).Msg("could not list users")
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users")))
return
}
groups := []string{}
groups := make([]string, 0, len(res.Groups))
for i := range res.Groups {
groups = append(groups, res.Groups[i].OnPremisesSamAccountName)
}
render.Render(w, r, response.DataRender(&data.Groups{Groups: groups}))
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: groups})))
}
// AddGroup adds a group
@@ -245,7 +260,7 @@ func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
if gid != "" {
gidNumber, err = strconv.ParseInt(gid, 10, 64)
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided")))
o.logger.Error().Err(err).Str("gid", gid).Str("groupid", groupid).Msg("Cannot use the gidnumber provided")
return
}
@@ -268,17 +283,17 @@ func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
case http.StatusConflict:
if response.APIVersion(r.Context()) == "2" {
// it seems the application framework sets the ocs status code to the httpstatus code, which affects the provisioning api
// see https://github.com/owncloud/core/blob/b9ff4c93e051c94adfb301545098ae627e52ef76/lib/public/AppFramework/OCSController.php#L142-L150
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
} else {
render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail)))
}
default:
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("groupid", groupid).Msg("could not add group")
// TODO check error if group already existed
@@ -286,7 +301,7 @@ func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
}
o.logger.Debug().Interface("group", group).Msg("added group")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// DeleteGroup deletes a group
@@ -298,9 +313,9 @@ func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
return
}
@@ -312,16 +327,16 @@ func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("groupid", group.Id).Msg("could not remove group")
return
}
o.logger.Debug().Str("groupid", group.Id).Msg("removed group")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// GetGroupMembers lists all members of a group
@@ -334,9 +349,9 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
return
}
@@ -346,21 +361,21 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("groupid", group.Id).Msg("could not get list of members")
return
}
members := []string{}
members := make([]string, 0, len(res.Members))
for i := range res.Members {
members = append(members, res.Members[i].OnPremisesSamAccountName)
}
o.logger.Error().Err(err).Int("count", len(members)).Str("groupid", groupid).Msg("listing group members")
render.Render(w, r, response.DataRender(&data.Users{Users: members}))
mustNotFail(render.Render(w, r, response.DataRender(&data.Users{Users: members})))
}
func isValidUUID(uuid string) bool {
@@ -381,3 +396,9 @@ func (o Ocs) fetchGroupByName(ctx context.Context, name string) (*accounts.Group
}
return nil, merrors.NotFound("", "The requested group could not be found")
}
func mustNotFail(err error) {
if err != nil {
panic(err)
}
}
+7 -1
View File
@@ -80,7 +80,7 @@ func VersionCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
version := chi.URLParam(r, "version")
if version == "" {
render.Render(w, r, ErrRender(data.MetaBadRequest.StatusCode, "unknown ocs api version"))
mustNotFail(render.Render(w, r, ErrRender(data.MetaBadRequest.StatusCode, "unknown ocs api version")))
return
}
w.Header().Set("Ocs-Api-Version", version)
@@ -90,3 +90,9 @@ func VersionCtx(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func mustNotFail(err error) {
if err != nil {
panic(err)
}
}
+17 -2
View File
@@ -19,6 +19,8 @@ import (
ocsm "github.com/owncloud/ocis/ocs/pkg/middleware"
"github.com/owncloud/ocis/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/ocs/pkg/service/v0/response"
"github.com/owncloud/ocis/proxy/pkg/cs3"
"github.com/owncloud/ocis/proxy/pkg/user/backend"
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
)
@@ -57,10 +59,15 @@ func NewService(opts ...Option) Service {
logger: options.Logger,
}
if svc.config.AccountBackend == "" {
svc.config.AccountBackend = "accounts"
}
requireUser := ocsm.RequireUser()
requireAdmin := ocsm.RequireAdmin(
ocsm.RoleManager(roleManager),
ocsm.Logger(options.Logger),
)
requireSelfOrAdmin := ocsm.RequireSelfOrAdmin(
@@ -146,18 +153,26 @@ func (o Ocs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// NotFound uses ErrRender to always return a proper OCS payload
func (o Ocs) NotFound(w http.ResponseWriter, r *http.Request) {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found")))
}
func (o Ocs) getAccountService() accounts.AccountsService {
return accounts.NewAccountsService("com.owncloud.api.accounts", grpc.DefaultClient)
}
func (o Ocs) getCS3Backend() backend.UserBackend {
revaClient, err := cs3.GetGatewayServiceClient(o.config.RevaAddress)
if err != nil {
o.logger.Fatal().Msgf("could not get reva client at address %s", o.config.RevaAddress)
}
return backend.NewCS3UserBackend(revaClient, nil, revaClient, o.logger)
}
func (o Ocs) getGroupsService() accounts.GroupsService {
return accounts.NewGroupsService("com.owncloud.api.accounts", grpc.DefaultClient)
}
// NotImplementedStub returns a not implemented error
func (o Ocs) NotImplementedStub(w http.ResponseWriter, r *http.Request) {
render.Render(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "Not implemented"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "Not implemented")))
}
+283 -68
View File
@@ -9,18 +9,28 @@ import (
"strconv"
"strings"
"github.com/cs3org/reva/pkg/user"
"github.com/go-chi/chi"
"github.com/go-chi/render"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/protobuf/types/known/fieldmaskpb"
"github.com/cs3org/reva/pkg/auth/scope"
"github.com/asim/go-micro/plugins/client/grpc/v3"
merrors "github.com/asim/go-micro/v3/errors"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
revauser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/token/manager/jwt"
"github.com/cs3org/reva/pkg/user"
"github.com/go-chi/chi"
"github.com/go-chi/render"
accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0"
"github.com/owncloud/ocis/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/ocs/pkg/service/v0/response"
storepb "github.com/owncloud/ocis/store/pkg/proto/v0"
"github.com/pkg/errors"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
// GetSelf returns the currently logged in user
@@ -29,7 +39,7 @@ func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
var err error
u, ok := user.ContextGetUser(r.Context())
if !ok || u.Id == nil || u.Id.OpaqueId == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
return
}
@@ -39,11 +49,19 @@ func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
// if the user was authenticated why was he not found?!? log error?
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
// TODO(someone) this fix is in place because if the user backend (PROXY_ACCOUNT_BACKEND_TYPE) is set to, for instance,
// cs3, we cannot count with the accounts service.
if u != nil {
d := &data.User{
UserID: u.Username,
DisplayName: u.DisplayName,
LegacyDisplayName: u.DisplayName,
Email: u.Mail,
UIDNumber: u.UidNumber,
GIDNumber: u.GidNumber,
}
mustNotFail(render.Render(w, r, response.DataRender(d)))
return
}
o.logger.Error().Err(merr).Interface("user", u).Msg("could not get account for user")
return
@@ -64,7 +82,7 @@ func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
GIDNumber: account.GidNumber,
// TODO hide enabled flag or it might get rendered as false
}
render.Render(w, r, response.DataRender(d))
mustNotFail(render.Render(w, r, response.DataRender(d)))
}
// GetUser returns the user with the given userid
@@ -73,17 +91,23 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
var account *accounts.Account
var err error
if userid == "" {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
} else {
switch {
case userid == "":
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context")))
case o.config.AccountBackend == "accounts":
account, err = o.fetchAccountByUsername(r.Context(), userid)
case o.config.AccountBackend == "cs3":
account, err = o.fetchAccountFromCS3Backend(r.Context(), userid)
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(merr).Str("userid", userid).Msg("could not get account for user")
return
@@ -120,7 +144,7 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
Definition: "default",
},
}
render.Render(w, r, response.DataRender(d))
mustNotFail(render.Render(w, r, response.DataRender(d)))
}
// AddUser creates a new user account
@@ -138,7 +162,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
if uid != "" {
uidNumber, err = strconv.ParseInt(uid, 10, 64)
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the uidnumber provided"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the uidnumber provided")))
o.logger.Error().Err(err).Str("userid", userid).Msg("Cannot use the uidnumber provided")
return
}
@@ -146,7 +170,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
if gid != "" {
gidNumber, err = strconv.ParseInt(gid, 10, 64)
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided")))
o.logger.Error().Err(err).Str("userid", userid).Msg("Cannot use the gidnumber provided")
return
}
@@ -180,24 +204,34 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
newAccount.GidNumber = gidNumber
}
account, err := o.getAccountService().CreateAccount(r.Context(), &accounts.CreateAccountRequest{
Account: newAccount,
})
var account *accounts.Account
switch o.config.AccountBackend {
case "accounts":
account, err = o.getAccountService().CreateAccount(r.Context(), &accounts.CreateAccountRequest{
Account: newAccount,
})
case "cs3":
o.logger.Fatal().Msg("cs3 backend doesn't support adding users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
case http.StatusConflict:
if response.APIVersion(r.Context()) == "2" {
// it seems the application framework sets the ocs status code to the httpstatus code, which affects the provisioning api
// see https://github.com/owncloud/core/blob/b9ff4c93e051c94adfb301545098ae627e52ef76/lib/public/AppFramework/OCSController.php#L142-L150
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
} else {
render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail)))
}
default:
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not add user")
// TODO check error if account already existed
@@ -217,7 +251,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
} else {
enabled = "false"
}
render.Render(w, r, response.DataRender(&data.User{
mustNotFail(render.Render(w, r, response.DataRender(&data.User{
UserID: account.OnPremisesSamAccountName,
DisplayName: account.DisplayName,
LegacyDisplayName: account.DisplayName,
@@ -225,19 +259,30 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
UIDNumber: account.UidNumber,
GIDNumber: account.GidNumber,
Enabled: enabled,
}))
})))
}
// EditUser creates a new user account
func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
userid := chi.URLParam(r, "userid")
account, err := o.fetchAccountByUsername(r.Context(), userid)
var account *accounts.Account
var err error
switch o.config.AccountBackend {
case "accounts":
account, err = o.fetchAccountByUsername(r.Context(), userid)
case "cs3":
o.logger.Fatal().Msg("cs3 backend doesn't support editing users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not edit user")
return
@@ -269,7 +314,7 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
req.UpdateMask = &fieldmaskpb.FieldMask{Paths: []string{"DisplayName"}}
default:
// https://github.com/owncloud/core/blob/24b7fa1d2604a208582055309a5638dbd9bda1d1/apps/provisioning_api/lib/Users.php#L321
render.Render(w, r, response.ErrRender(103, "unknown key '"+key+"'"))
mustNotFail(render.Render(w, r, response.ErrRender(103, "unknown key '"+key+"'")))
return
}
@@ -278,9 +323,9 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
default:
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("account_id", req.Account.Id).Str("user_id", userid).Msg("could not edit user")
return
@@ -292,24 +337,123 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
}
o.logger.Debug().Interface("account", account).Msg("updated user")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// DeleteUser deletes a user
func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
userid := chi.URLParam(r, "userid")
account, err := o.fetchAccountByUsername(r.Context(), userid)
var account *accounts.Account
var err error
switch o.config.AccountBackend {
case "accounts":
account, err = o.fetchAccountByUsername(r.Context(), userid)
case "cs3":
o.logger.Fatal().Msg("cs3 backend doesn't support deleting users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not delete user")
return
}
if o.config.RevaAddress != "" && o.config.StorageUsersDriver != "owncloud" {
t, err := o.mintTokenForUser(r.Context(), account)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "error minting token").Error())))
return
}
ctx := metadata.AppendToOutgoingContext(r.Context(), token.TokenHeader, t)
gwc, err := pool.GetGatewayServiceClient(o.config.RevaAddress)
if err != nil {
o.logger.Error().Err(err).Msg("error securing a connection to Reva gateway")
}
homeResp, err := gwc.GetHome(ctx, &provider.GetHomeRequest{})
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not get home").Error())))
return
}
if homeResp.Status.Code != rpcv1beta1.Code_CODE_OK {
o.logger.Error().
Str("stat_status_code", homeResp.Status.Code.String()).
Str("stat_message", homeResp.Status.Message).
Msg("DeleteUser: could not get user home: get failed")
return
}
statResp, err := gwc.Stat(ctx, &provider.StatRequest{
Ref: &provider.Reference{
Path: homeResp.Path,
},
})
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not stat home").Error())))
return
}
if statResp.Status.Code != rpcv1beta1.Code_CODE_OK {
o.logger.Error().
Str("stat_status_code", statResp.Status.Code.String()).
Str("stat_message", statResp.Status.Message).
Msg("DeleteUser: could not delete user home: stat failed")
return
}
delReq := &provider.DeleteRequest{
Ref: &provider.Reference{
ResourceId: statResp.Info.Id,
},
}
delResp, err := gwc.Delete(ctx, delReq)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete home").Error())))
return
}
if delResp.Status.Code != rpcv1beta1.Code_CODE_OK {
o.logger.Error().
Str("stat_status_code", statResp.Status.Code.String()).
Str("stat_message", statResp.Status.Message).
Msg("DeleteUser: could not delete user home: delete failed")
return
}
req := &gateway.PurgeRecycleRequest{
Ref: &provider.Reference{
Path: homeResp.Path,
},
}
purgeRecycleResponse, err := gwc.PurgeRecycle(ctx, req)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete trash").Error())))
return
}
if purgeRecycleResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
o.logger.Error().
Str("stat_status_code", statResp.Status.Code.String()).
Str("stat_message", statResp.Status.Message).
Msg("DeleteUser: could not delete user trash: delete failed")
return
}
}
req := accounts.DeleteAccountRequest{
Id: account.Id,
}
@@ -318,28 +462,62 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", req.Id).Msg("could not delete user")
return
}
o.logger.Debug().Str("userid", req.Id).Msg("deleted user")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// TODO(refs) this to ocis-pkg ... we are minting tokens all over the place ... or use a service? ... like reva?
func (o Ocs) mintTokenForUser(ctx context.Context, account *accounts.Account) (string, error) {
tm, _ := jwt.New(map[string]interface{}{
"secret": o.config.TokenManager.JWTSecret,
"expires": int64(60),
})
u := &revauser.User{
Id: &revauser.UserId{
OpaqueId: account.Id,
Idp: o.config.IdentityManagement.Address,
},
Groups: []string{},
UidNumber: account.UidNumber,
GidNumber: account.GidNumber,
}
s, err := scope.GetOwnerScope()
if err != nil {
return "", err
}
return tm.MintToken(ctx, u, s)
}
// EnableUser enables a user
func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
userid := chi.URLParam(r, "userid")
account, err := o.fetchAccountByUsername(r.Context(), userid)
var account *accounts.Account
var err error
switch o.config.AccountBackend {
case "accounts":
account, err = o.fetchAccountByUsername(r.Context(), userid)
case "cs3":
o.logger.Fatal().Msg("cs3 backend doesn't support enabling users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not enable user")
return
@@ -358,28 +536,39 @@ func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("account_id", account.Id).Msg("could not enable account")
return
}
o.logger.Debug().Str("account_id", account.Id).Msg("enabled user")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// DisableUser disables a user
func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
userid := chi.URLParam(r, "userid")
account, err := o.fetchAccountByUsername(r.Context(), userid)
var account *accounts.Account
var err error
switch o.config.AccountBackend {
case "accounts":
account, err = o.fetchAccountByUsername(r.Context(), userid)
case "cs3":
o.logger.Fatal().Msg("cs3 backend doesn't support disabling users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not disable user")
return
@@ -398,16 +587,16 @@ func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found")))
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
}
o.logger.Error().Err(err).Str("account_id", account.Id).Msg("could not disable account")
return
}
o.logger.Debug().Str("account_id", account.Id).Msg("disabled user")
render.Render(w, r, response.DataRender(struct{}{}))
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
}
// GetSigningKey returns the signing key for the current user. It will create it on the fly if it does not exist
@@ -417,7 +606,7 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
u, ok := user.ContextGetUser(r.Context())
if !ok {
//o.logger.Error().Msg("missing user in context")
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context")))
return
}
@@ -433,10 +622,10 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
Key: userID,
})
if err == nil && len(res.Records) > 0 {
render.Render(w, r, response.DataRender(&data.SigningKey{
mustNotFail(render.Render(w, r, response.DataRender(&data.SigningKey{
User: userID,
SigningKey: string(res.Records[0].Value),
}))
})))
return
}
if err != nil {
@@ -444,7 +633,7 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
if e.Code == http.StatusNotFound {
// not found is ok, so we can continue and generate the key on the fly
} else {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "error reading from store"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "error reading from store")))
return
}
}
@@ -453,7 +642,7 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
key := make([]byte, 64)
_, err = rand.Read(key[:])
if err != nil {
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not generate signing key"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not generate signing key")))
return
}
signingKey := hex.EncodeToString(key)
@@ -472,14 +661,14 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
if err != nil {
//o.logger.Error().Err(err).Msg("error writing key")
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not persist signing key"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not persist signing key")))
return
}
render.Render(w, r, response.DataRender(&data.SigningKey{
mustNotFail(render.Render(w, r, response.DataRender(&data.SigningKey{
User: userID,
SigningKey: signingKey,
}))
})))
}
// ListUsers lists the users
@@ -490,21 +679,32 @@ func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
query = fmt.Sprintf("on_premises_sam_account_name eq '%s'", escapeValue(search))
}
res, err := o.getAccountService().ListAccounts(r.Context(), &accounts.ListAccountsRequest{
Query: query,
})
var res *accounts.ListAccountsResponse
var err error
switch o.config.AccountBackend {
case "accounts":
res, err = o.getAccountService().ListAccounts(r.Context(), &accounts.ListAccountsRequest{
Query: query,
})
case "cs3":
// TODO
o.logger.Fatal().Msg("cs3 backend doesn't support listing users")
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
if err != nil {
o.logger.Err(err).Msg("could not list users")
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users")))
return
}
users := []string{}
users := make([]string, 0, len(res.Accounts))
for i := range res.Accounts {
users = append(users, res.Accounts[i].OnPremisesSamAccountName)
}
render.Render(w, r, response.DataRender(&data.Users{Users: users}))
mustNotFail(render.Render(w, r, response.DataRender(&data.Users{Users: users})))
}
// escapeValue escapes all special characters in the value
@@ -525,3 +725,18 @@ func (o Ocs) fetchAccountByUsername(ctx context.Context, name string) (*accounts
}
return nil, merrors.NotFound("", "The requested user could not be found")
}
func (o Ocs) fetchAccountFromCS3Backend(ctx context.Context, name string) (*accounts.Account, error) {
backend := o.getCS3Backend()
u, err := backend.GetUserByClaims(ctx, "username", name, false)
if err != nil {
return nil, err
}
return &accounts.Account{
OnPremisesSamAccountName: u.Username,
DisplayName: u.DisplayName,
Mail: u.Mail,
UidNumber: u.UidNumber,
GidNumber: u.GidNumber,
}, nil
}
+90
View File
@@ -0,0 +1,90 @@
package tracing
import (
"time"
"contrib.go.opencensus.io/exporter/jaeger"
"contrib.go.opencensus.io/exporter/ocagent"
"contrib.go.opencensus.io/exporter/zipkin"
openzipkin "github.com/openzipkin/zipkin-go"
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocs/pkg/config"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
func Configure(cfg *config.Config, logger log.Logger) error {
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,
Process: jaeger.Process{
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")
}
return nil
}
+4 -2
View File
@@ -7,8 +7,9 @@ geekdocEditPath: edit/master/ocs/templates
geekdocFilePath: CONFIGURATION.tmpl
---
{{- define "options"}}
{{ $fnName := (last . ).Flags -}}
{{ range $opt := first . }}{{ with list $fnName $opt -}}
{{ $fnNames := (last . ).Flags -}}
{{ range $opt := first . }}
{{ range $fnName := $fnNames }}{{ with list $fnName $opt -}}
{{ $o := last . -}}
{{ if eq $o.FnName $fnName -}}
-{{ $o.Name }} | {{ range $i, $e := $o.Env }} {{ if $i }}, {{ end }}${{ $e }}{{ end }}
@@ -17,6 +18,7 @@ geekdocFilePath: CONFIGURATION.tmpl
{{ end -}}
{{ end -}}
{{ end -}}
{{ end -}}
{{ end }}
{{`{{< toc >}}`}}
-8
View File
@@ -1,8 +0,0 @@
// +build tools
package main
import (
_ "github.com/UnnoTed/fileb0x"
_ "github.com/restic/calens"
)