walk and log chi routes, ocs cleanup (#5428)

* walk and log chi routes, ocs cleanup

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* make linter happy

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Jörn Friedrich Dreyer
2023-01-23 12:30:34 +01:00
committed by GitHub
parent 473c64fe03
commit 32cc9beb0b
25 changed files with 59 additions and 681 deletions

View File

@@ -270,6 +270,11 @@ func NewService(opts ...Option) (Graph, error) {
})
})
_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
return svc, nil
}

View File

@@ -238,6 +238,11 @@ func (idp *IDP) initMux(ctx context.Context, r []server.WithRoutes, h http.Handl
idp.mux.Get("/signin/v1/identifier/index.html", idp.Index())
idp.mux.Mount("/", gm)
_ = chi.Walk(idp.mux, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
}
// ServeHTTP implements the Service interface.

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
"github.com/owncloud/ocis/v2/ocis-pkg/version"
"github.com/owncloud/ocis/v2/services/ocs/pkg/config/parser"
"github.com/owncloud/ocis/v2/services/ocs/pkg/logging"
@@ -34,10 +33,6 @@ func Server(cfg *config.Config) *cli.Command {
if err != nil {
return err
}
err = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
if err != nil {
return err
}
var (
gr = run.Group{}

View File

@@ -19,21 +19,7 @@ type Config struct {
HTTP HTTP `yaml:"http"`
TokenManager *TokenManager `yaml:"token_manager"`
Reva *shared.Reva `yaml:"reva"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
IdentityManagement IdentityManagement `yaml:"identity_management"`
AccountBackend string `yaml:"-"` // we only support cs3 backend, no need to have this configurable
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;OCS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
TokenManager *TokenManager `yaml:"token_manager"`
Context context.Context `yaml:"-"`
}
// 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 `yaml:"address" env:"OCIS_URL;OCIS_OIDC_ISSUER;OCS_IDM_ADDRESS" desc:"URL of the OIDC issuer. It defaults to URL of the builtin IDP."`
}

View File

@@ -3,7 +3,6 @@ package defaults
import (
"strings"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
)
@@ -36,11 +35,6 @@ func DefaultConfig() *config.Config {
Service: config.Service{
Name: "ocs",
},
AccountBackend: "cs3",
Reva: shared.DefaultRevaConfig(),
IdentityManagement: config.IdentityManagement{
Address: "https://localhost:9200",
},
}
}
@@ -78,15 +72,6 @@ func EnsureDefaults(cfg *config.Config) {
cfg.CacheStore = &config.CacheStore{}
}
if cfg.Reva == nil && cfg.Commons != nil && cfg.Commons.Reva != nil {
cfg.Reva = &shared.Reva{
Address: cfg.Commons.Reva.Address,
TLS: cfg.Commons.Reva.TLS,
}
} else if cfg.Reva == nil {
cfg.Reva = &shared.Reva{}
}
if cfg.TokenManager == nil && cfg.Commons != nil && cfg.Commons.TokenManager != nil {
cfg.TokenManager = &config.TokenManager{
JWTSecret: cfg.Commons.TokenManager.JWTSecret,
@@ -95,17 +80,6 @@ func EnsureDefaults(cfg *config.Config) {
cfg.TokenManager = &config.TokenManager{}
}
if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
}
if cfg.GRPCClientTLS == nil {
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
if cfg.Commons != nil && cfg.Commons.GRPCClientTLS != nil {
cfg.GRPCClientTLS.Mode = cfg.Commons.GRPCClientTLS.Mode
cfg.GRPCClientTLS.CACert = cfg.Commons.GRPCClientTLS.CACert
}
}
if cfg.Commons != nil {
cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
}

View File

@@ -39,9 +39,5 @@ func Validate(cfg *config.Config) error {
return shared.MissingJWTTokenError(cfg.Service.Name)
}
if cfg.MachineAuthAPIKey == "" {
return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
}
return nil
}

View File

@@ -1,42 +0,0 @@
package middleware
import (
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
// Logger to use for logging, must be set
Logger log.Logger
// RoleManager for looking up permissions
RoleManager *roles.Manager
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(l log.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// RoleManager provides a function to set the RoleManager option.
func RoleManager(val *roles.Manager) Option {
return func(o *Options) {
o.RoleManager = val
}
}

View File

@@ -1,42 +0,0 @@
package middleware
import (
"net/http"
"github.com/go-chi/render"
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
)
// RequireAdmin middleware is used to require the user in context to be an admin / have account management permissions
func RequireAdmin(opts ...Option) func(next http.Handler) http.Handler {
opt := newOptions(opts...)
mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
if err := render.Render(w, r, renderer); err != nil {
opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
}
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// get roles from context
roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context())
if !ok {
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
// check if permission is present in roles of the authenticated account
if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.AccountManagementPermissionID) != nil {
next.ServeHTTP(w, r)
return
}
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
})
}
}

View File

@@ -1,81 +0,0 @@
package middleware
import (
"net/http"
"net/url"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
)
// RequireSelfOrAdmin middleware is used to require the requesting user to be an admin or the requested user himself
func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
opt := newOptions(opts...)
mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
if err := render.Render(w, r, renderer); err != nil {
opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
}
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
mustRender(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 {
opt.Logger.Debug().Str("userid", u.Id.OpaqueId).Msg("No roles in context, contacting settings service")
var err error
roleIDs, err = opt.RoleManager.FindRoleIDsForUser(r.Context(), u.Id.OpaqueId)
if err != nil {
opt.Logger.Err(err).Str("userid", u.Id.OpaqueId).Msg("failed to get roles for user")
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
if len(roleIDs) == 0 {
roleIDs = append(roleIDs, settings.BundleUUIDRoleUser, settings.SelfManagementPermissionID)
// if roles are empty, assume we haven't seen the user before and assign a default user role. At least until
// proper roles are provided. See https://github.com/owncloud/ocis/v2/issues/1825 for more context.
//return user, nil
}
}
// check if account management permission is present in roles of the authenticated account
if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.AccountManagementPermissionID) != nil {
next.ServeHTTP(w, r)
return
}
// check if self management permission is present in roles of the authenticated account
if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.SelfManagementPermissionID) != nil {
userid := chi.URLParam(r, "userid")
var err error
if userid, err = url.PathUnescape(userid); err != nil {
mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "malformed username"))
}
if userid == "" || userid == u.Id.OpaqueId || userid == u.Username {
next.ServeHTTP(w, r)
return
}
}
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
})
}
}

View File

@@ -1,39 +0,0 @@
package middleware
import (
"net/http"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/go-chi/render"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
)
// RequireUser middleware is used to require a user in context
func RequireUser(opts ...Option) func(next http.Handler) http.Handler {
opt := newOptions(opts...)
mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
if err := render.Render(w, r, renderer); err != nil {
opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
}
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
return
}
next.ServeHTTP(w, r)
})
}
}

View File

@@ -1,19 +0,0 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
)
// GetConfig renders the ocs config endpoint
func (o Ocs) GetConfig(w http.ResponseWriter, r *http.Request) {
o.mustRender(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
}))
}

View File

@@ -1,10 +0,0 @@
package data
// ConfigData holds basic config
type ConfigData struct {
Version string `json:"version" xml:"version"`
Website string `json:"website" xml:"website"`
Host string `json:"host" xml:"host"`
Contact string `json:"contact" xml:"contact"`
SSL string `json:"ssl" xml:"ssl"`
}

View File

@@ -1,6 +0,0 @@
package data
// Groups holds group ids for the groups listing
type Groups struct {
Groups []string `json:"groups" xml:"groups>element"`
}

View File

@@ -1,31 +1,5 @@
package data
// Users holds user ids for the user listing
type Users struct {
Users []string `json:"users" xml:"users>element"`
}
// User holds the payload for a GetUser response
type User struct {
Enabled string `json:"enabled" xml:"enabled"`
UserID string `json:"id" xml:"id"` // UserID is mapped to the preferred_name attribute in accounts
DisplayName string `json:"display-name" xml:"display-name"`
LegacyDisplayName string `json:"displayname" xml:"displayname"`
Email string `json:"email" xml:"email"`
Quota *Quota `json:"quota" xml:"quota"`
UIDNumber int64 `json:"uidnumber" xml:"uidnumber"`
GIDNumber int64 `json:"gidnumber" xml:"gidnumber"`
}
// Quota holds quota information
type Quota struct {
Free int64 `json:"free" xml:"free"`
Used int64 `json:"used" xml:"used"`
Total int64 `json:"total" xml:"total"`
Relative float32 `json:"relative" xml:"relative"`
Definition string `json:"definition" xml:"definition"`
}
// SigningKey holds the Payload for a GetSigningKey response
type SigningKey struct {
User string `json:"user" xml:"user"`

View File

@@ -1,88 +0,0 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
)
const (
_backendCS3 = "cs3"
)
// ListUserGroups lists a users groups
func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
// TODO
o.mustRender(w, r, response.DataRender(&data.Groups{}))
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// AddToGroup adds a user to a group
func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
// TODO
o.cs3WriteNotSupported(w, r)
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// RemoveFromGroup removes a user from a group
func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
// TODO
o.cs3WriteNotSupported(w, r)
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// ListGroups lists all groups
func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
// TODO
o.mustRender(w, r, response.DataRender(&data.Groups{}))
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// AddGroup adds a group
// oC10 implementation: https://github.com/owncloud/core/blob/762780a23c9eadda4fb5fa8db99eba66a5100b6e/apps/provisioning_api/lib/Groups.php#L126-L154
func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
o.cs3WriteNotSupported(w, r)
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// DeleteGroup deletes a group
func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
o.cs3WriteNotSupported(w, r)
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// GetGroupMembers lists all members of a group
func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case _backendCS3:
// TODO
o.mustRender(w, r, response.DataRender(&data.Users{}))
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}

View File

@@ -23,8 +23,3 @@ type instrument struct {
func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
i.next.ServeHTTP(w, r)
}
// GetConfig implements the Service interface.
func (i instrument) GetConfig(w http.ResponseWriter, r *http.Request) {
i.next.GetConfig(w, r)
}

View File

@@ -23,8 +23,3 @@ type logging struct {
func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
l.next.ServeHTTP(w, r)
}
// GetConfig implements the Service interface.
func (l logging) GetConfig(w http.ResponseWriter, r *http.Request) {
l.next.GetConfig(w, r)
}

View File

@@ -4,8 +4,6 @@ import (
"net/http"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
)
@@ -14,11 +12,9 @@ type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Config *config.Config
Middleware []func(http.Handler) http.Handler
RoleService settingssvc.RoleService
RoleManager *roles.Manager
Logger log.Logger
Config *config.Config
Middleware []func(http.Handler) http.Handler
}
// newOptions initializes the available default options.
@@ -52,17 +48,3 @@ func Middleware(val ...func(http.Handler) http.Handler) Option {
o.Middleware = val
}
}
// RoleService provides a function to set the RoleService option.
func RoleService(val settingssvc.RoleService) Option {
return func(o *Options) {
o.RoleService = val
}
}
// RoleManager provides a function to set the RoleManager option.
func RoleManager(val *roles.Manager) Option {
return func(o *Options) {
o.RoleManager = val
}
}

View File

@@ -3,10 +3,6 @@ package svc
import (
"net/http"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
"github.com/owncloud/ocis/v2/ocis-pkg/store"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
@@ -14,19 +10,15 @@ import (
"github.com/owncloud/ocis/v2/ocis-pkg/account"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
opkgm "github.com/owncloud/ocis/v2/ocis-pkg/middleware"
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
ocsm "github.com/owncloud/ocis/v2/services/ocs/pkg/middleware"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
"github.com/owncloud/ocis/v2/services/proxy/pkg/user/backend"
)
// Service defines the service handlers.
type Service interface {
ServeHTTP(http.ResponseWriter, *http.Request)
GetConfig(http.ResponseWriter, *http.Request)
}
// NewService returns a service implementation for Service.
@@ -36,40 +28,12 @@ func NewService(opts ...Option) Service {
m := chi.NewMux()
m.Use(options.Middleware...)
roleService := options.RoleService
if roleService == nil {
roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
}
roleManager := options.RoleManager
if roleManager == nil {
storeOptions := store.OcisStoreOptions{
Type: options.Config.CacheStore.Type,
Address: options.Config.CacheStore.Address,
Size: options.Config.CacheStore.Size,
}
m := roles.NewManager(
roles.StoreOptions(storeOptions),
roles.Logger(options.Logger),
roles.RoleService(roleService),
)
roleManager = &m
}
svc := Ocs{
config: options.Config,
mux: m,
RoleManager: roleManager,
logger: options.Logger,
config: options.Config,
mux: m,
logger: options.Logger,
}
if svc.config.AccountBackend == "" {
svc.config.AccountBackend = "cs3"
}
requireUser := ocsm.RequireUser(
ocsm.Logger(options.Logger),
)
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
r.NotFound(svc.NotFound)
r.Use(middleware.StripSlashes)
@@ -80,31 +44,23 @@ func NewService(opts ...Option) Service {
r.Use(ocsm.OCSFormatCtx) // updates request Accept header according to format=(json|xml) query parameter
r.Route("/v{version:(1|2)}.php", func(r chi.Router) {
r.Use(response.VersionCtx) // stores version in context
r.Route("/apps/files_sharing/api/v1", func(r chi.Router) {})
r.Route("/apps/notifications/api/v1", func(r chi.Router) {})
r.Route("/cloud", func(r chi.Router) {
r.Route("/capabilities", func(r chi.Router) {})
// TODO /apps
r.Route("/user", func(r chi.Router) {
r.Get("/signing-key", svc.GetSigningKey)
})
})
r.Route("/config", func(r chi.Router) {
r.With(requireUser).Get("/", svc.GetConfig)
})
r.Get("/cloud/user/signing-key", svc.GetSigningKey)
})
})
_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
return svc
}
// Ocs defines implements the business logic for Service.
type Ocs struct {
config *config.Config
logger log.Logger
RoleService settingssvc.RoleService
RoleManager *roles.Manager
mux *chi.Mux
config *config.Config
logger log.Logger
mux *chi.Mux
}
// ServeHTTP implements the Service interface.
@@ -117,19 +73,6 @@ func (o Ocs) NotFound(w http.ResponseWriter, r *http.Request) {
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found"))
}
func (o Ocs) getCS3Backend() backend.UserBackend {
revaClient, err := pool.GetGatewayServiceClient(o.config.Reva.Address, o.config.Reva.GetRevaOptions()...)
if err != nil {
o.logger.Fatal().Msgf("could not get reva client at address %s", o.config.Reva.Address)
}
return backend.NewCS3UserBackend(nil, revaClient, o.config.MachineAuthAPIKey, "", nil, o.logger)
}
// NotImplementedStub returns a not implemented error
func (o Ocs) NotImplementedStub(w http.ResponseWriter, r *http.Request) {
o.mustRender(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "Not implemented"))
}
func (o Ocs) mustRender(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
if err := render.Render(w, r, renderer); err != nil {
o.logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)

View File

@@ -21,8 +21,3 @@ type tracing struct {
func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
middleware.TraceContext(t.next).ServeHTTP(w, r)
}
// GetConfig implements the Service interface.
func (t tracing) GetConfig(w http.ResponseWriter, r *http.Request) {
t.next.GetConfig(w, r)
}

View File

@@ -1,155 +1,20 @@
package svc
import (
"context"
"crypto/rand"
"encoding/hex"
"net/http"
"net/url"
storemsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/store/v0"
storesvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/store/v0"
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/go-chi/chi/v5"
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
ocstracing "github.com/owncloud/ocis/v2/services/ocs/pkg/tracing"
merrors "go-micro.dev/v4/errors"
)
// GetSelf returns the currently logged in user
func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok || u.Id == nil || u.Id.OpaqueId == "" {
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
return
}
d := &data.User{
UserID: u.Username,
DisplayName: u.DisplayName,
LegacyDisplayName: u.DisplayName,
Email: u.Mail,
UIDNumber: u.UidNumber,
GIDNumber: u.GidNumber,
}
o.mustRender(w, r, response.DataRender(d))
}
// GetUser returns the user with the given userid
func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
userid := chi.URLParam(r, "userid")
userid, err := url.PathUnescape(userid)
if err != nil {
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
var user *cs3.User
switch {
case userid == "":
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
case o.config.AccountBackend == "cs3":
user, 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 {
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, data.MessageUserNotFound))
} else {
o.mustRender(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
}
o.logger.Debug().Interface("user", user).Msg("got user")
d := &data.User{
UserID: user.Username,
DisplayName: user.DisplayName,
LegacyDisplayName: user.DisplayName,
Email: user.Mail,
UIDNumber: user.UidNumber,
GIDNumber: user.GidNumber,
Enabled: "true", // TODO include in response only when admin?
// TODO query storage registry for free space? of home storage, maybe...
Quota: &data.Quota{
Free: 2840756224000,
Used: 5059416668,
Total: 2845815640668,
Relative: 0.18,
Definition: "default",
},
}
_, span := ocstracing.TraceProvider.
Tracer("ocs").
Start(r.Context(), "GetUser")
defer span.End()
o.mustRender(w, r, response.DataRender(d))
}
// AddUser creates a new user account
func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// EditUser creates a new user account
func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// DeleteUser deletes a user
func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// EnableUser enables a user
func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// DisableUser disables a user
func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
// GetSigningKey returns the signing key for the current user. It will create it on the fly if it does not exist
// The signing key is part of the user settings and is used by the proxy to authenticate requests
// Currently, the username is used as the OC-Credential
@@ -222,29 +87,3 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
SigningKey: signingKey,
}))
}
// ListUsers lists the users
func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
switch o.config.AccountBackend {
case "cs3":
// TODO
o.cs3WriteNotSupported(w, r)
return
default:
o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
}
}
func (o Ocs) fetchAccountFromCS3Backend(ctx context.Context, name string) (*cs3.User, error) {
backend := o.getCS3Backend()
u, _, err := backend.GetUserByClaims(ctx, "username", name, false)
if err != nil {
return nil, err
}
return u, nil
}
func (o Ocs) cs3WriteNotSupported(w http.ResponseWriter, r *http.Request) {
o.logger.Warn().Msg("the CS3 backend does not support adding or updating users")
o.NotImplementedStub(w, r)
}

View File

@@ -2,13 +2,14 @@ package http
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
chimiddleware "github.com/go-chi/chi/v5/middleware"
"github.com/owncloud/ocis/v2/ocis-pkg/account"
"github.com/owncloud/ocis/v2/ocis-pkg/cors"
"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
ohttp "github.com/owncloud/ocis/v2/ocis-pkg/service/http"
"github.com/owncloud/ocis/v2/ocis-pkg/version"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/assets"
@@ -17,24 +18,24 @@ import (
)
// Server initializes the http service and server.
func Server(opts ...Option) (http.Service, error) {
func Server(opts ...Option) (ohttp.Service, error) {
options := newOptions(opts...)
service, err := http.NewService(
http.TLSConfig(options.Config.HTTP.TLS),
http.Logger(options.Logger),
http.Name(options.Name),
http.Version(version.GetString()),
http.Address(options.Config.HTTP.Addr),
http.Namespace(options.Config.HTTP.Namespace),
http.Context(options.Context),
http.Flags(options.Flags...),
service, err := ohttp.NewService(
ohttp.TLSConfig(options.Config.HTTP.TLS),
ohttp.Logger(options.Logger),
ohttp.Name(options.Name),
ohttp.Version(version.GetString()),
ohttp.Address(options.Config.HTTP.Addr),
ohttp.Namespace(options.Config.HTTP.Namespace),
ohttp.Context(options.Context),
ohttp.Flags(options.Flags...),
)
if err != nil {
options.Logger.Error().
Err(err).
Msg("Error initializing http service")
return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
return ohttp.Service{}, fmt.Errorf("could not initialize http service: %w", err)
}
handle := svc.NewService(options.Config, options.Logger)
@@ -88,6 +89,11 @@ func Server(opts ...Option) (http.Service, error) {
settingssvc.RegisterPermissionServiceWeb(r, handle)
})
_ = chi.Walk(mux, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
micro.RegisterHandler(service.Server(), mux)
return service, nil

View File

@@ -55,6 +55,11 @@ func NewService(opts ...Option) Service {
r.Get("/data", svc.GetThumbnail)
})
_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
return svc
}

View File

@@ -45,6 +45,11 @@ func NewService(opts ...Option) Service {
r.Mount("/", svc.Static(options.Config.HTTP.CacheTTL))
})
_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
return svc
}

View File

@@ -134,6 +134,11 @@ func NewService(opts ...Option) (Service, error) {
}))
})
_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
return nil
})
return svc, nil
}