diff --git a/changelog/unreleased/fix-enforce-password.md b/changelog/unreleased/fix-enforce-password.md new file mode 100644 index 00000000000..1a2f4180998 --- /dev/null +++ b/changelog/unreleased/fix-enforce-password.md @@ -0,0 +1,6 @@ +Bugfix: fix enforce password + +We fixed enforce password when normal users can update the public link to delete its password if permission is not sent in data + +https://github.com/owncloud/ocis/pull/7862 +https://github.com/owncloud/ocis/issues/7821 diff --git a/go.mod b/go.mod index 8cf950af5a4..ae1399def1e 100644 --- a/go.mod +++ b/go.mod @@ -346,3 +346,5 @@ require ( ) replace github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a + +replace github.com/cs3org/reva/v2 => github.com/2403905/reva/v2 v2.0.0-20231130202014-0a07b4f916db diff --git a/go.sum b/go.sum index a234ba17035..f47f1ad597a 100644 --- a/go.sum +++ b/go.sum @@ -764,6 +764,8 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/2403905/reva/v2 v2.0.0-20231130202014-0a07b4f916db h1:SPf/aQJufM4w4ar+cBXfcMqjbECW9u3p2tVN3DG+mbk= +github.com/2403905/reva/v2 v2.0.0-20231130202014-0a07b4f916db/go.mod h1:zcrrYVsBv/DwhpyO2/W5hoSZ/k6az6Z2EYQok65uqZY= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= @@ -1017,8 +1019,6 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9 h1:5vKQcL1hPHEZKu9e8C9rl0ap3ofMBznmoSgi4lRYXec= -github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9/go.mod h1:zcrrYVsBv/DwhpyO2/W5hoSZ/k6az6Z2EYQok65uqZY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go index 092d6c3b4a0..4a68298df77 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go @@ -69,7 +69,7 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request, } // NOTE: one is allowed to create an internal link without the `Publink.Write` permission - if permKey != nil && *permKey != 0 { + if permKey != 0 { ok, err := utils.CheckPermission(ctx, permission.WritePublicLink, c) if err != nil { return nil, &ocsError{ @@ -124,8 +124,8 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request, // default perms: read-only // TODO: the default might change depending on allowed permissions and configs - if permKey == nil { - permKey = &_defaultPublicLinkPermission + if permKey == 0 { + permKey = _defaultPublicLinkPermission } permissions, err := ocPublicPermToCs3(permKey) if err != nil { @@ -137,7 +137,7 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request, } password := r.FormValue("password") - if h.enforcePassword(permKey) && len(password) == 0 { + if h.enforcePassword(&permKey) && len(password) == 0 { return nil, &ocsError{ Code: response.MetaBadRequest.StatusCode, Message: "missing required password", @@ -326,7 +326,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar createdByUser := publicshare.IsCreatedByUser(*share, user) // NOTE: you are allowed to update a link TO a public link without the `PublicLink.Write` permission if you created it yourself - if (permKey != nil && *permKey != 0) || !createdByUser { + if permKey != 0 || !createdByUser { ok, err := utils.CheckPermission(ctx, permission.WritePublicLink, gwC) if err != nil { response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to check user permission", err) @@ -454,8 +454,8 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar // Password newPassword, ok := r.Form["password"] // enforcePassword - if h.enforcePassword(permKey) { - p, err := conversions.NewPermissions(decreasePermissionsIfNecessary(*permKey)) + if h.enforcePassword(&permKey) { + p, err := conversions.NewPermissions(decreasePermissionsIfNecessary(permKey)) if err != nil { response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to check permissions from request", err) return @@ -616,12 +616,8 @@ func decreasePermissionsIfNecessary(perm int) int { return perm } -func ocPublicPermToCs3(pk *int) (*provider.ResourcePermissions, error) { - if pk == nil { - return nil, nil - } - - permKey := decreasePermissionsIfNecessary(*pk) +func ocPublicPermToCs3(pk int) (*provider.ResourcePermissions, error) { + permKey := decreasePermissionsIfNecessary(pk) // TODO refactor this ocPublicPermToRole[permKey] check into a conversions.NewPublicSharePermissions? // not all permissions are possible for public shares @@ -640,7 +636,7 @@ func ocPublicPermToCs3(pk *int) (*provider.ResourcePermissions, error) { } // pointer will be nil if no permission is set -func permKeyFromRequest(r *http.Request, h *Handler) (*int, error) { +func permKeyFromRequest(r *http.Request, h *Handler) (int, error) { var err error // phoenix sends: {"permissions": 15}. See ocPublicPermToRole struct for mapping @@ -655,7 +651,7 @@ func permKeyFromRequest(r *http.Request, h *Handler) (*int, error) { publicUploadFlag, err := strconv.ParseBool(publicUploadString) if err != nil { log.Error().Err(err).Str("publicUpload", publicUploadString).Msg("could not parse publicUpload argument") - return nil, err + return 0, err } if publicUploadFlag { @@ -666,17 +662,17 @@ func permKeyFromRequest(r *http.Request, h *Handler) (*int, error) { permissionsString := r.FormValue("permissions") if permissionsString == "" { // no permission values given - return nil, nil + return 0, nil } permKey, err = strconv.Atoi(permissionsString) if err != nil { log.Error().Str("permissionFromRequest", "shares").Msgf("invalid type: %T", permKey) - return nil, fmt.Errorf("invalid type: %T", permKey) + return 0, fmt.Errorf("invalid type: %T", permKey) } } - return &permKey, nil + return permKey, nil } // checkPasswordEnforcement checks if the password needs to be set for a link diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/recycle.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/recycle.go index eeb153777a4..72c2b226403 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/recycle.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/recycle.go @@ -27,6 +27,9 @@ import ( "strings" "time" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/pkg/appctx" @@ -35,7 +38,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/storagespace" - "github.com/pkg/errors" ) // Recycle items are stored inside the node folder and start with the uuid of the deleted node. @@ -214,66 +216,111 @@ func readTrashLink(path string) (string, string, string, error) { func (fs *Decomposedfs) listTrashRoot(ctx context.Context, spaceID string) ([]*provider.RecycleItem, error) { log := appctx.GetLogger(ctx) - items := make([]*provider.RecycleItem, 0) - trashRoot := fs.getRecycleRoot(spaceID) - matches, err := filepath.Glob(trashRoot + "/*/*/*/*/*") + + subTrees, err := filepath.Glob(trashRoot + "/*") if err != nil { return nil, err } - for _, itemPath := range matches { - nodePath, nodeID, timeSuffix, err := readTrashLink(itemPath) - if err != nil { - log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Msg("error reading trash link, skipping") - continue - } + numWorkers := fs.o.MaxConcurrency + if len(subTrees) < numWorkers { + numWorkers = len(subTrees) + } - md, err := os.Stat(nodePath) - if err != nil { - log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("could not stat trash item, skipping") - continue - } + work := make(chan string, len(subTrees)) + results := make(chan *provider.RecycleItem, len(subTrees)) - attrs, err := fs.lu.MetadataBackend().All(ctx, nodePath) - if err != nil { - log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("could not get extended attributes, skipping") - continue - } + g, ctx := errgroup.WithContext(ctx) - nodeType := fs.lu.TypeFromPath(ctx, nodePath) - if nodeType == provider.ResourceType_RESOURCE_TYPE_INVALID { - log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("invalid node type, skipping") - continue - } - - item := &provider.RecycleItem{ - Type: nodeType, - Size: uint64(md.Size()), - Key: nodeID, - } - if deletionTime, err := time.Parse(time.RFC3339Nano, timeSuffix); err == nil { - item.DeletionTime = &types.Timestamp{ - Seconds: uint64(deletionTime.Unix()), - // TODO nanos + // Distribute work + g.Go(func() error { + defer close(work) + for _, itemPath := range subTrees { + select { + case work <- itemPath: + case <-ctx.Done(): + return ctx.Err() } - } else { - log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not parse time format, ignoring") } + return nil + }) + + // Spawn workers that'll concurrently work the queue + for i := 0; i < numWorkers; i++ { + g.Go(func() error { + for subTree := range work { + matches, err := filepath.Glob(subTree + "/*/*/*/*") + if err != nil { + return err + } + + for _, itemPath := range matches { + nodePath, nodeID, timeSuffix, err := readTrashLink(itemPath) + if err != nil { + log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Msg("error reading trash link, skipping") + continue + } + + md, err := os.Stat(nodePath) + if err != nil { + log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("could not stat trash item, skipping") + continue + } + + attrs, err := fs.lu.MetadataBackend().All(ctx, nodePath) + if err != nil { + log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("could not get extended attributes, skipping") + continue + } + + nodeType := fs.lu.TypeFromPath(ctx, nodePath) + if nodeType == provider.ResourceType_RESOURCE_TYPE_INVALID { + log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("invalid node type, skipping") + continue + } + + item := &provider.RecycleItem{ + Type: nodeType, + Size: uint64(md.Size()), + Key: nodeID, + } + if deletionTime, err := time.Parse(time.RFC3339Nano, timeSuffix); err == nil { + item.DeletionTime = &types.Timestamp{ + Seconds: uint64(deletionTime.Unix()), + // TODO nanos + } + } else { + log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not parse time format, ignoring") + } + + // lookup origin path in extended attributes + if attr, ok := attrs[prefixes.TrashOriginAttr]; ok { + item.Ref = &provider.Reference{Path: string(attr)} + } else { + log.Error().Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not read origin path") + } + select { + case results <- item: + case <-ctx.Done(): + return ctx.Err() + } + } + } + return nil + }) + } - // lookup origin path in extended attributes - if attr, ok := attrs[prefixes.TrashOriginAttr]; ok { - item.Ref = &provider.Reference{Path: string(attr)} - } else { - log.Error().Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not read origin path, skipping") - continue - } - // TODO filter results by permission ... on the original parent? or the trashed node? - // if it were on the original parent it would be possible to see files that were trashed before the current user got access - // so -> check the trash node itself - // hmm listing trash currently lists the current users trash or the 'root' trash. from ocs only the home storage is queried for trash items. - // for now we can only really check if the current user is the owner - items = append(items, item) + // Wait for things to settle down, then close results chan + go func() { + _ = g.Wait() // error is checked later + close(results) + }() + + // Collect results + items := []*provider.RecycleItem{} + for ri := range results { + items = append(items, ri) } return items, nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 43e862eb8ac..6369cd01999 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -357,7 +357,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9 +# github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9 => github.com/2403905/reva/v2 v2.0.0-20231130202014-0a07b4f916db ## explicit; go 1.20 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime @@ -2276,3 +2276,4 @@ stash.kopano.io/kgol/oidc-go ## explicit; go 1.13 stash.kopano.io/kgol/rndm # github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a +# github.com/cs3org/reva/v2 => github.com/2403905/reva/v2 v2.0.0-20231130202014-0a07b4f916db