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
-19
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
}))
}
@@ -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"`
}
@@ -1,6 +0,0 @@
package data
// Groups holds group ids for the groups listing
type Groups struct {
Groups []string `json:"groups" xml:"groups>element"`
}
-26
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"`
-88
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)
}
}
@@ -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)
}
-5
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)
}
+3 -21
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
}
}
+12 -69
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)
-5
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)
}
-161
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)
}