Merge branch 'master' into no-additional-init
This commit is contained in:
+10
-142
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+34
-6
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 >}}`}}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// +build tools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/UnnoTed/fileb0x"
|
||||
_ "github.com/restic/calens"
|
||||
)
|
||||
Reference in New Issue
Block a user