Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Share expiration #3594

Merged
merged 6 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/unreleased/share-expiration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add expiration to user and group shares

Added expiration to user and group shares. When shares are accessed after expiration the share is automatically removed.

https://github.com/cs3org/reva/pull/3594
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,5 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/cs3org/go-cs3apis => github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35 h1:bbpRY/l4z5MTH+TRGZdkIqDM9JXQQewJdO1o+80zcok=
github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
Expand Down Expand Up @@ -245,8 +247,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965 h1:y4n2j68LLnvac+zw/al8MfPgO5aQiIwLmHM/JzYN8AM=
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
2 changes: 1 addition & 1 deletion internal/grpc/services/gateway/ocmshareprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (s *svc) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareRequest
}

if s.c.CommitShareToStorageGrant {
addGrantStatus, err := s.addGrant(ctx, req.ResourceId, req.Grant.Grantee, req.Grant.Permissions.Permissions, nil)
addGrantStatus, err := s.addGrant(ctx, req.ResourceId, req.Grant.Grantee, req.Grant.Permissions.Permissions, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "gateway: error adding OCM grant to storage")
}
Expand Down
7 changes: 4 additions & 3 deletions internal/grpc/services/gateway/usershareprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func (s *svc) denyGrant(ctx context.Context, id *provider.ResourceId, g *provide
return grantRes.Status, nil
}

func (s *svc) addGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
func (s *svc) addGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, expiration *typesv1beta1.Timestamp, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
ref := &provider.Reference{
ResourceId: id,
}
Expand All @@ -401,6 +401,7 @@ func (s *svc) addGrant(ctx context.Context, id *provider.ResourceId, g *provider
Grantee: g,
Permissions: p,
Creator: creator.GetId(),
Expiration: expiration,
},
Opaque: opaque,
}
Expand Down Expand Up @@ -568,7 +569,7 @@ func (s *svc) addShare(ctx context.Context, req *collaboration.CreateShareReques
return nil, errors.Wrap(err, "gateway: error denying grant in storage")
}
} else {
status, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, nil)
status, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, nil)
if err != nil {
return nil, errors.Wrap(err, "gateway: error adding grant to storage")
}
Expand Down Expand Up @@ -609,7 +610,7 @@ func (s *svc) addSpaceShare(ctx context.Context, req *collaboration.CreateShareR
return nil, errors.Wrap(err, "gateway: error denying grant in storage")
}
} else {
st, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, opaque)
st, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, opaque)
if err != nil {
return nil, errors.Wrap(err, "gateway: error adding grant to storage")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (s *service) ListShares(ctx context.Context, req *collaboration.ListSharesR
}

func (s *service) UpdateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) {
share, err := s.sm.UpdateShare(ctx, req.Ref, req.Field.GetPermissions()) // TODO(labkode): check what to update
share, err := s.sm.UpdateShare(ctx, req.Ref, req.Field.GetPermissions(), req.Share, req.UpdateMask) // TODO(labkode): check what to update
if err != nil {
return &collaboration.UpdateShareResponse{
Status: status.NewInternal(ctx, "error updating share"),
Expand Down
9 changes: 9 additions & 0 deletions internal/http/services/owncloud/ocs/conversions/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ const (

// ShareWithUserTypeGuest represents a guest user
ShareWithUserTypeGuest ShareWithUserType = 1

// The datetime format of ISO8601
_iso8601 = "2006-01-02T15:04:05Z0700"
)

// ResourceType indicates the OCS type of the resource
Expand Down Expand Up @@ -242,6 +245,12 @@ func CS3Share2ShareData(ctx context.Context, share *collaboration.Share) (*Share
if share.Ctime != nil {
sd.STime = share.Ctime.Seconds // TODO CS3 api birth time = btime
}

if share.Expiration != nil {
expiration := time.Unix(int64(share.Expiration.Seconds), int64(share.Expiration.Nanos))
sd.Expiration = expiration.Format(_iso8601)
}

return sd, nil
}

Expand Down
7 changes: 4 additions & 3 deletions internal/http/services/owncloud/ocs/data/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,10 @@ type CapabilitiesFilesSharingPublicExpireDate struct {

// CapabilitiesFilesSharingUser TODO document
type CapabilitiesFilesSharingUser struct {
SendMail ocsBool `json:"send_mail" xml:"send_mail" mapstructure:"send_mail"`
ProfilePicture ocsBool `json:"profile_picture" xml:"profile_picture" mapstructure:"profile_picture"`
Settings []*CapabilitiesUserSettings `json:"settings" xml:"settings" mapstructure:"settings"`
SendMail ocsBool `json:"send_mail" xml:"send_mail" mapstructure:"send_mail"`
ProfilePicture ocsBool `json:"profile_picture" xml:"profile_picture" mapstructure:"profile_picture"`
Settings []*CapabilitiesUserSettings `json:"settings" xml:"settings" mapstructure:"settings"`
ExpireDate *CapabilitiesFilesSharingPublicExpireDate `json:"expire_date" xml:"expire_date" mapstructure:"expire_date"`
}

// CapabilitiesUserSettings holds available user settings service information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog"
"google.golang.org/grpc/metadata"
Expand Down Expand Up @@ -729,21 +730,36 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return
}

shareR.Share.Permissions = &collaboration.SharePermissions{Permissions: role.CS3ResourcePermissions()}

var fieldMaskPaths = []string{"permissions"}

expireDate := r.PostFormValue("expireDate")
var expirationTs *types.Timestamp
if expireDate != "" {

expiration, err := time.Parse(time.RFC3339, expireDate)
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "could not parse expireDate", err)
return
}
expirationTs = &types.Timestamp{
Seconds: uint64(expiration.UnixNano() / int64(time.Second)),
Nanos: uint32(expiration.UnixNano() % int64(time.Second)),
}

shareR.Share.Expiration = expirationTs
fieldMaskPaths = append(fieldMaskPaths, "expiration")
} else if r.Form.Has("expireDate") {
// If the expiration parameter was sent but is empty, then the expiration should be removed.
shareR.Share.Expiration = nil
fieldMaskPaths = append(fieldMaskPaths, "expiration")
}

uReq := &collaboration.UpdateShareRequest{
Ref: &collaboration.ShareReference{
Spec: &collaboration.ShareReference_Id{
Id: &collaboration.ShareId{
OpaqueId: shareID,
},
},
},
Field: &collaboration.UpdateShareRequest_UpdateField{
Field: &collaboration.UpdateShareRequest_UpdateField_Permissions{
Permissions: &collaboration.SharePermissions{
// this completely overwrites the permissions for this user
Permissions: role.CS3ResourcePermissions(),
},
},
Share: shareR.Share,
UpdateMask: &fieldmaskpb.FieldMask{
Paths: fieldMaskPaths,
},
}
uRes, err := client.UpdateShare(ctx, uReq)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package shares

import (
"net/http"
"time"

userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
Expand All @@ -34,6 +35,10 @@ import (
"github.com/cs3org/reva/v2/pkg/utils"
)

const (
_iso8601 = "2006-01-02T15:04:05Z0700"
)

func (h *Handler) createUserShare(w http.ResponseWriter, r *http.Request, statInfo *provider.ResourceInfo, role *conversions.Role, roleVal []byte) (*collaboration.Share, *ocsError) {
ctx := r.Context()
c, err := h.getClient()
Expand Down Expand Up @@ -75,6 +80,26 @@ func (h *Handler) createUserShare(w http.ResponseWriter, r *http.Request, statIn
}
}

expireDate := r.PostFormValue("expireDate")
var expirationTs *types.Timestamp
if expireDate != "" {
// FIXME: the web ui sends the RFC3339 format when updating a share but
// initially on creating a share the format ISO 8601 is used.
// OC10 uses RFC3339 in both cases so we should fix the web ui and change it here.
expiration, err := time.Parse(_iso8601, expireDate)
if err != nil {
return nil, &ocsError{
Code: response.MetaBadRequest.StatusCode,
Message: "could not parse expireDate",
Error: err,
}
}
expirationTs = &types.Timestamp{
Seconds: uint64(expiration.UnixNano() / int64(time.Second)),
Nanos: uint32(expiration.UnixNano() % int64(time.Second)),
}
}

createShareReq := &collaboration.CreateShareRequest{
Opaque: &types.Opaque{
Map: map[string]*types.OpaqueEntry{
Expand All @@ -93,6 +118,7 @@ func (h *Handler) createUserShare(w http.ResponseWriter, r *http.Request, statIn
Permissions: &collaboration.SharePermissions{
Permissions: role.CS3ResourcePermissions(),
},
Expiration: expirationTs,
},
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cbox/share/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) er
return nil
}

func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) {
func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
permissions := conversions.SharePermToInt(p.Permissions)
uid := conversions.FormatUserID(ctxpkg.ContextMustGetUser(ctx).Id)

Expand Down
2 changes: 1 addition & 1 deletion pkg/share/manager/cs3/cs3.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ func (m *Manager) ListShares(ctx context.Context, filters []*collaboration.Filte
}

// UpdateShare updates the mode of the given share.
func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) {
func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
if err := m.initialize(); err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/share/manager/cs3/cs3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ var _ = Describe("Manager", func() {
Expect(share.Permissions.Permissions.AddGrant).To(BeFalse())
s, err := m.UpdateShare(ctx,
&collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Id}},
&collaboration.SharePermissions{Permissions: &provider.ResourcePermissions{AddGrant: true}})
&collaboration.SharePermissions{Permissions: &provider.ResourcePermissions{AddGrant: true}}, nil, nil)
Expect(err).ToNot(HaveOccurred())
Expect(s).ToNot(BeNil())
Expect(s.Permissions.Permissions.AddGrant).To(BeTrue())
Expand Down
42 changes: 36 additions & 6 deletions pkg/share/manager/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,21 +373,51 @@ func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) er
return nil
}

func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) {
func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
m.Lock()
defer m.Unlock()
idx, s, err := m.get(ref)
if err != nil {
return nil, err

var (
idx int
toUpdate *collaboration.Share
)

if ref != nil {
var err error
idx, toUpdate, err = m.get(ref)
if err != nil {
return nil, err
}
} else if updated != nil {
var err error
idx, toUpdate, err = m.getByID(updated.Id)
if err != nil {
return nil, err
}
}

if fieldMask != nil {
for i := range fieldMask.Paths {
switch fieldMask.Paths[i] {
case "permissions":
m.model.Shares[idx].Permissions = updated.Permissions
case "expiration":
m.model.Shares[idx].Expiration = updated.Expiration
default:
return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported")
}
}
}

user := ctxpkg.ContextMustGetUser(ctx)
if !share.IsCreatedByUser(s, user) {
if !share.IsCreatedByUser(toUpdate, user) {
return nil, errtypes.NotFound(ref.String())
}

now := time.Now().UnixNano()
m.model.Shares[idx].Permissions = p
if p != nil {
m.model.Shares[idx].Permissions = p
}
m.model.Shares[idx].Mtime = &typespb.Timestamp{
Seconds: uint64(now / int64(time.Second)),
Nanos: uint32(now % int64(time.Second)),
Expand Down
Loading