Merge pull request #2825 from owncloud/fix-create-group-no-name

[full-ci] Bugfix: Disallow creation of a group with empty name via the OCS api
This commit is contained in:
Phil Davis
2021-11-28 17:04:18 +05:45
committed by GitHub
10 changed files with 168 additions and 139 deletions

View File

@@ -0,0 +1,10 @@
Bugfix: Disallow creation of a group with empty name via the OCS api
We've fixed the behavior for group creation on the OCS api, where it was
possible to create a group with an empty name. This was is not possible
on oC10 and is therefore also forbidden on oCIS to keep compatibility.
This PR forbids the creation and also ensures the correct status code
for both OCS v1 and OCS v2 apis.
https://github.com/owncloud/ocis/pull/2825
https://github.com/owncloud/ocis/issues/2823

View File

@@ -14,13 +14,19 @@ import (
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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
@@ -30,13 +36,7 @@ func RequireAdmin(opts ...Option) func(next http.Handler) http.Handler {
return
}
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
})
}
}
func mustNotFail(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -17,22 +17,28 @@ import (
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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
@@ -47,7 +53,7 @@ func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
userid := chi.URLParam(r, "userid")
var err error
if userid, err = url.PathUnescape(userid); err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "malformed username")))
mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "malformed username"))
}
if userid == "" || userid == u.Id.OpaqueId || userid == u.Username {
@@ -56,7 +62,7 @@ func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
}
}
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
})
}

View File

@@ -10,18 +10,25 @@ import (
)
// RequireUser middleware is used to require a user in context
func RequireUser() func(next http.Handler) http.Handler {
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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized")))
mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
return
}
if u.Id == nil || u.Id.OpaqueId == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
return
}

View File

@@ -3,18 +3,17 @@ package svc
import (
"net/http"
"github.com/go-chi/render"
"github.com/owncloud/ocis/ocs/pkg/service/v0/data"
"github.com/owncloud/ocis/ocs/pkg/service/v0/response"
)
// GetConfig renders the ocs config endpoint
func (o Ocs) GetConfig(w http.ResponseWriter, r *http.Request) {
mustNotFail(render.Render(w, r, response.DataRender(&data.ConfigData{
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

@@ -11,7 +11,6 @@ import (
revactx "github.com/cs3org/reva/pkg/ctx"
"github.com/go-chi/chi/v5"
"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"
@@ -41,7 +40,7 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
span.SetAttributes(attribute.StringSlice("groups", u.Groups))
if len(u.Groups) > 0 {
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: u.Groups})))
o.mustRender(w, r, response.DataRender(&data.Groups{Groups: u.Groups}))
return
}
}
@@ -57,9 +56,9 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -98,26 +97,31 @@ func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
span.SetAttributes(attribute.StringSlice("groups", groups))
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: groups})))
o.mustRender(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) {
mustNotFail(r.ParseForm())
err := r.ParseForm()
if err != nil {
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Could not parse form from request"))
return
}
userid := chi.URLParam(r, "userid")
groupid := r.PostForm.Get("groupid")
if groupid == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty group assignment: unspecified group")))
o.mustRender(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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
return
}
@@ -127,9 +131,9 @@ func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
return
}
@@ -142,16 +146,16 @@ func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// RemoveFromGroup removes a user from a group
@@ -165,23 +169,23 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
// read it manually
body, err := ioutil.ReadAll(r.Body)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error()))
return
}
if err = r.Body.Close(); err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
return
}
values, err := url.ParseQuery(string(body))
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, err.Error()))
return
}
groupid := values.Get("groupid")
if groupid == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "no group id")))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "no group id"))
return
}
@@ -197,9 +201,9 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -211,9 +215,9 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
return
}
@@ -226,16 +230,16 @@ func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// ListGroups lists all groups
@@ -252,7 +256,7 @@ func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
if err != nil {
o.logger.Err(err).Msg("could not list users")
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
return
}
@@ -268,31 +272,43 @@ func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
span.SetAttributes(attribute.StringSlice("groups", groups))
mustNotFail(render.Render(w, r, response.DataRender(&data.Groups{Groups: groups})))
o.mustRender(w, r, response.DataRender(&data.Groups{Groups: groups}))
}
// 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) {
groupid := r.PostFormValue("groupid")
displayname := r.PostFormValue("displayname")
gid := r.PostFormValue("gidnumber")
if displayname == "" && groupid == "" {
code := data.MetaFailure.StatusCode // v1
if response.APIVersion(r.Context()) == "2" {
code = data.MetaBadRequest.StatusCode
}
o.mustRender(w, r, response.ErrRender(code, "No groupid or display name provided"))
return
}
if displayname == "" {
// oC10 OCS does not know about a group displayname
// therefore we fall back to the oC10 parameter groupid (which is the groupname in the oC10 world)
displayname = groupid
}
var gidNumber int64
var err error
if gid != "" {
gidNumber, err = strconv.ParseInt(gid, 10, 64)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided")))
o.mustRender(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
}
}
if displayname == "" {
displayname = groupid
}
newGroup := &accounts.Group{
Id: groupid,
DisplayName: displayname,
@@ -306,17 +322,17 @@ func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
o.mustRender(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
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail)))
o.mustRender(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail))
}
default:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -324,7 +340,7 @@ func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
}
o.logger.Debug().Interface("group", group).Msg("added group")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// DeleteGroup deletes a group
@@ -336,9 +352,9 @@ func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
return
}
@@ -350,16 +366,16 @@ func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// GetGroupMembers lists all members of a group
@@ -372,9 +388,9 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
return
}
@@ -384,9 +400,9 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -398,7 +414,7 @@ func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
}
o.logger.Error().Err(err).Int("count", len(members)).Str("groupid", groupid).Msg("listing group members")
mustNotFail(render.Render(w, r, response.DataRender(&data.Users{Users: members})))
o.mustRender(w, r, response.DataRender(&data.Users{Users: members}))
}
func isValidUUID(uuid string) bool {
@@ -419,9 +435,3 @@ 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)
}
}

View File

@@ -80,7 +80,7 @@ func VersionCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
version := chi.URLParam(r, "version")
if version == "" {
mustNotFail(render.Render(w, r, ErrRender(data.MetaBadRequest.StatusCode, "unknown ocs api version")))
_ = render.Render(w, r, ErrRender(data.MetaBadRequest.StatusCode, "unknown ocs api version"))
return
}
w.Header().Set("Ocs-Api-Version", version)
@@ -90,9 +90,3 @@ func VersionCtx(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func mustNotFail(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -63,7 +63,9 @@ func NewService(opts ...Option) Service {
svc.config.AccountBackend = "accounts"
}
requireUser := ocsm.RequireUser()
requireUser := ocsm.RequireUser(
ocsm.Logger(options.Logger),
)
requireAdmin := ocsm.RequireAdmin(
ocsm.RoleManager(roleManager),
@@ -153,7 +155,7 @@ 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) {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found"))
}
func (o Ocs) getAccountService() accounts.AccountsService {
@@ -174,5 +176,11 @@ func (o Ocs) getGroupsService() accounts.GroupsService {
// NotImplementedStub returns a not implemented error
func (o Ocs) NotImplementedStub(w http.ResponseWriter, r *http.Request) {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "Not implemented")))
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

@@ -18,7 +18,6 @@ import (
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/token/manager/jwt"
"github.com/go-chi/chi/v5"
"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"
@@ -37,7 +36,7 @@ func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
var err error
u, ok := revactx.ContextGetUser(r.Context())
if !ok || u.Id == nil || u.Id.OpaqueId == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id")))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
return
}
@@ -58,7 +57,7 @@ func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
UIDNumber: u.UidNumber,
GIDNumber: u.GidNumber,
}
mustNotFail(render.Render(w, r, response.DataRender(d)))
o.mustRender(w, r, response.DataRender(d))
return
}
o.logger.Error().Err(merr).Interface("user", u).Msg("could not get account for user")
@@ -80,7 +79,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
}
mustNotFail(render.Render(w, r, response.DataRender(d)))
o.mustRender(w, r, response.DataRender(d))
}
// GetUser returns the user with the given userid
@@ -91,7 +90,7 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
switch {
case userid == "":
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context")))
o.mustRender(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":
@@ -103,9 +102,9 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
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
@@ -148,7 +147,7 @@ func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
Start(r.Context(), "GetUser")
defer span.End()
mustNotFail(render.Render(w, r, response.DataRender(d)))
o.mustRender(w, r, response.DataRender(d))
}
// AddUser creates a new user account
@@ -166,7 +165,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
if uid != "" {
uidNumber, err = strconv.ParseInt(uid, 10, 64)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the uidnumber provided")))
o.mustRender(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
}
@@ -174,13 +173,13 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
if gid != "" {
gidNumber, err = strconv.ParseInt(gid, 10, 64)
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided")))
o.mustRender(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
}
}
if strings.TrimSpace(password) == "" {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty password not allowed")))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty password not allowed"))
o.logger.Error().Str("userid", userid).Msg("empty password not allowed")
return
}
@@ -230,17 +229,17 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
o.mustRender(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
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail)))
o.mustRender(w, r, response.ErrRender(data.MetaInvalidInput.StatusCode, merr.Detail))
}
default:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -260,7 +259,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
} else {
enabled = "false"
}
mustNotFail(render.Render(w, r, response.DataRender(&data.User{
o.mustRender(w, r, response.DataRender(&data.User{
UserID: account.OnPremisesSamAccountName,
DisplayName: account.DisplayName,
LegacyDisplayName: account.DisplayName,
@@ -268,7 +267,7 @@ func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
UIDNumber: account.UidNumber,
GIDNumber: account.GidNumber,
Enabled: enabled,
})))
}))
}
// EditUser creates a new user account
@@ -289,9 +288,9 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not edit user")
return
@@ -323,7 +322,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
mustNotFail(render.Render(w, r, response.ErrRender(103, "unknown key '"+key+"'")))
o.mustRender(w, r, response.ErrRender(103, "unknown key '"+key+"'"))
return
}
@@ -332,9 +331,9 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
merr := merrors.FromError(err)
switch merr.Code {
case http.StatusBadRequest:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail)))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
default:
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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
@@ -346,7 +345,7 @@ func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
}
o.logger.Debug().Interface("account", account).Msg("updated user")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// DeleteUser deletes a user
@@ -367,9 +366,9 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not delete user")
return
@@ -378,7 +377,7 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
if o.config.Reva.Address != "" && 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())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "error minting token").Error()))
return
}
@@ -391,7 +390,7 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
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())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not get home").Error()))
return
}
@@ -410,7 +409,7 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not stat home").Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not stat home").Error()))
return
}
@@ -430,7 +429,7 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
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())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete home").Error()))
return
}
@@ -450,7 +449,7 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
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())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete trash").Error()))
return
}
@@ -471,16 +470,16 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(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?
@@ -524,9 +523,9 @@ func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not enable user")
return
@@ -545,16 +544,16 @@ func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(w, r, response.DataRender(struct{}{}))
}
// DisableUser disables a user
@@ -575,9 +574,9 @@ func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
}
o.logger.Error().Err(err).Str("userid", userid).Msg("could not disable user")
return
@@ -596,16 +595,16 @@ func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
if err != nil {
merr := merrors.FromError(err)
if merr.Code == http.StatusNotFound {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found")))
o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested account could not be found"))
} else {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error())))
o.mustRender(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")
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
o.mustRender(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
@@ -615,7 +614,7 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
//o.logger.Error().Msg("missing user in context")
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context")))
o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
return
}
@@ -631,10 +630,10 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
Key: userID,
})
if err == nil && len(res.Records) > 0 {
mustNotFail(render.Render(w, r, response.DataRender(&data.SigningKey{
o.mustRender(w, r, response.DataRender(&data.SigningKey{
User: userID,
SigningKey: string(res.Records[0].Value),
})))
}))
return
}
if err != nil {
@@ -642,7 +641,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 {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "error reading from store")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "error reading from store"))
return
}
}
@@ -651,7 +650,7 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
key := make([]byte, 64)
_, err = rand.Read(key[:])
if err != nil {
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not generate signing key")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not generate signing key"))
return
}
signingKey := hex.EncodeToString(key)
@@ -670,14 +669,14 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
if err != nil {
//o.logger.Error().Err(err).Msg("error writing key")
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not persist signing key")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not persist signing key"))
return
}
mustNotFail(render.Render(w, r, response.DataRender(&data.SigningKey{
o.mustRender(w, r, response.DataRender(&data.SigningKey{
User: userID,
SigningKey: signingKey,
})))
}))
}
// ListUsers lists the users
@@ -704,7 +703,7 @@ func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
if err != nil {
o.logger.Err(err).Msg("could not list users")
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users")))
o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
return
}
@@ -713,7 +712,7 @@ func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
users = append(users, res.Accounts[i].OnPremisesSamAccountName)
}
mustNotFail(render.Render(w, r, response.DataRender(&data.Users{Users: users})))
o.mustRender(w, r, response.DataRender(&data.Users{Users: users}))
}
// escapeValue escapes all special characters in the value

View File

@@ -983,10 +983,6 @@ _ocs: api compatibility, return correct status code_
- [apiProvisioningGroups-v1/addGroup.feature:134](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L134)
- [apiProvisioningGroups-v2/addGroup.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L129)
#### [creating a group with empty name doesn't give an error](https://github.com/owncloud/ocis/issues/2823)
- [apiProvisioningGroups-v1/addGroup.feature:181](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L181)
- [apiProvisioningGroups-v2/addGroup.feature:177](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L177)
#### [cannot create group with '/'](https://github.com/owncloud/product/issues/285)
- [apiProvisioningGroups-v1/addToGroup.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L82)
- [apiProvisioningGroups-v1/deleteGroup.feature:85](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L85)