Skip to content

Commit e9f14e0

Browse files
committed
Implement shared ID for public links and shares
1 parent d4c5866 commit e9f14e0

File tree

4 files changed

+107
-33
lines changed

4 files changed

+107
-33
lines changed

share/model.go

+31-10
Original file line numberDiff line numberDiff line change
@@ -30,34 +30,55 @@ func (i ItemType) String() string {
3030
return string(i)
3131
}
3232

33+
// ShareID only contains IDs of shares and public links. This is because OCIS requires
34+
// that shares and public links do not share an ID, so we need a shared table to make sure
35+
// that there are no duplicates.
36+
// This is implemented by having ShareID have an ID that is auto-increment, and shares and
37+
// public links will have their ID be a foreign key to ShareID
38+
// When creating a new share, we will then first create an ID entry and use this for the ID
39+
40+
type ShareID struct {
41+
ID uint `gorm:"primarykey"`
42+
}
43+
44+
// We cannot use gorm.Model, because we want our ID to be a foreign key to ShareID
45+
type BaseModel struct {
46+
// Id has to be called Id and not ID, otherwise the foreign key will not work
47+
// ID is a special field in GORM, which it uses as the default Primary Key
48+
Id uint `gorm:"uniqueIndex"`
49+
ShareId ShareID `gorm:"foreignKey:Id;references:ID;constraint:OnDelete:CASCADE"` //;references:ID
50+
CreatedAt time.Time
51+
UpdatedAt time.Time
52+
DeletedAt gorm.DeletedAt `gorm:"index"`
53+
}
54+
3355
// ProtoShare contains fields that are shared between PublicLinks and Shares.
3456
// Unfortunately, because these are shared, we cannot name our indexes
3557
// because then two indexes with the same name would be created
3658
type ProtoShare struct {
3759
// Including gorm.Model will embed a number of gorm-default fields
38-
gorm.Model
60+
BaseModel
3961
UIDOwner string `gorm:"size:64"`
4062
UIDInitiator string `gorm:"size:64"`
4163
ItemType ItemType `gorm:"size:16;index:"` // file | folder | reference | symlink
4264
InitialPath string
43-
Inode string `gorm:"size:32;index:"`
44-
Instance string `gorm:"size:32;index:"`
65+
Inode string `gorm:"primaryKey;size:32;index:"`
66+
Instance string `gorm:"primaryKey;size:32;index:"`
4567
Permissions uint8
4668
Orphan bool
4769
Expiration datatypes.NullTime
4870
}
4971

5072
type Share struct {
5173
ProtoShare
52-
ShareWith string `gorm:"size:255;index:i_share_with"` // 255 because this can be a lw account, which are mapped from email addresses / ...
74+
ShareWith string `gorm:"primaryKey;size:255;index:i_share_with"` // 255 because this can be a lw account, which are mapped from email addresses / ...
5375
SharedWithIsGroup bool
5476
Description string `gorm:"size:1024"`
5577
}
5678

5779
type PublicLink struct {
5880
ProtoShare
59-
Token string `gorm:"index:i_token"`
60-
// Enforce uniqueness in db re: Itemsource
81+
Token string `gorm:"primaryKey;index:i_token"`
6182
Quicklink bool
6283
NotifyUploads bool
6384
NotifyUploadsExtraRecipients string
@@ -68,8 +89,8 @@ type PublicLink struct {
6889

6990
type ShareState struct {
7091
gorm.Model
71-
ShareID uint `gorm:"foreignKey:ShareID;references:ID;uniqueIndex:i_shareid_user"` // Define the foreign key field
72-
Share Share // Define the association
92+
ShareID uint `gorm:"uniqueIndex:i_shareid_user"` // Define the foreign key field
93+
Share Share `gorm:"foreignKey:ShareID;references:Id"` // Define the association
7394
// Can not be uid because of lw accs
7495
User string `gorm:"uniqueIndex:i_shareid_user;size:255"`
7596
Synced bool
@@ -86,7 +107,7 @@ func (s *Share) AsCS3Share(granteeType userpb.UserType) *collaboration.Share {
86107
}
87108
return &collaboration.Share{
88109
Id: &collaboration.ShareId{
89-
OpaqueId: strconv.FormatUint(uint64(s.ID), 10),
110+
OpaqueId: strconv.FormatUint(uint64(s.Id), 10),
90111
},
91112
//ResourceId: &provider.Reference{StorageId: s.Prefix, NodeId: s.ItemSource},
92113
ResourceId: &provider.ResourceId{
@@ -139,7 +160,7 @@ func (p *PublicLink) AsCS3PublicShare() *link.PublicShare {
139160
}
140161
return &link.PublicShare{
141162
Id: &link.PublicShareId{
142-
OpaqueId: strconv.Itoa(int(p.ID)),
163+
OpaqueId: strconv.Itoa(int(p.Id)),
143164
},
144165
ResourceId: &provider.ResourceId{
145166
StorageId: p.Instance,

share/sql/common.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sql
33
import (
44
"fmt"
55

6+
model "github.com/cernbox/reva-plugins/share"
67
"github.com/cs3org/reva"
78
"gorm.io/driver/mysql"
89
"gorm.io/driver/sqlite"
@@ -33,14 +34,28 @@ func init() {
3334
}
3435

3536
func getDb(c config) (*gorm.DB, error) {
37+
gormCfg := &gorm.Config{
38+
DisableForeignKeyConstraintWhenMigrating: false,
39+
}
3640
switch c.Engine {
3741
case "sqlite":
38-
return gorm.Open(sqlite.Open(c.DBName), &gorm.Config{})
42+
return gorm.Open(sqlite.Open(c.DBName), gormCfg)
3943
case "mysql":
4044
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName)
41-
return gorm.Open(mysql.Open(dsn), &gorm.Config{})
45+
return gorm.Open(mysql.Open(dsn), gormCfg)
4246
default: // default is mysql
4347
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName)
44-
return gorm.Open(mysql.Open(dsn), &gorm.Config{})
48+
return gorm.Open(mysql.Open(dsn), gormCfg)
49+
}
50+
}
51+
52+
func createID(db *gorm.DB) (uint, error) {
53+
id := &model.ShareID{}
54+
55+
res := db.Create(&id)
56+
if res.Error != nil {
57+
return 0, res.Error
58+
} else {
59+
return id.ID, nil
4560
}
4661
}

share/sql/public_link.go

+39-12
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ func (m *publicShareMgr) CreatePublicShare(ctx context.Context, u *user.User, md
106106
NotifyUploadsExtraRecipients: notifyUploadsExtraRecipients,
107107
}
108108

109+
// Create Shared ID
110+
id, err := createID(m.db)
111+
if err != nil {
112+
return nil, errors.Wrap(err, "failed to create id for PublicShare")
113+
}
114+
115+
publiclink.BaseModel = model.BaseModel{
116+
Id: id,
117+
ShareId: model.ShareID{ID: id},
118+
}
119+
109120
publiclink.UIDOwner = conversions.FormatUserID(md.Owner)
110121
publiclink.UIDInitiator = conversions.FormatUserID(user.Id)
111122
publiclink.InitialPath = md.Path
@@ -155,30 +166,46 @@ func (m *publicShareMgr) UpdatePublicShare(ctx context.Context, u *user.User, re
155166
var res *gorm.DB
156167
switch req.GetUpdate().GetType() {
157168
case link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME:
158-
res = m.db.Model(&publiclink).Update("link_name", req.Update.GetDisplayName())
169+
res = m.db.Model(&publiclink).
170+
Where("id = ?", publiclink.Id).
171+
Update("link_name", req.Update.GetDisplayName())
159172
case link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS:
160173
permissions := conversions.SharePermToInt(req.Update.GetGrant().GetPermissions().Permissions)
161-
res = m.db.Model(&publiclink).Update("permissions", uint8(permissions))
174+
res = m.db.Model(&publiclink).
175+
Where("id = ?", publiclink.Id).
176+
Update("permissions", uint8(permissions))
162177
case link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION:
163-
res = m.db.Model(&publiclink).Update("expiration", time.Unix(int64(req.Update.GetGrant().Expiration.Seconds), 0))
178+
res = m.db.Model(&publiclink).
179+
Where("id = ?", publiclink.Id).
180+
Update("expiration", time.Unix(int64(req.Update.GetGrant().Expiration.Seconds), 0))
164181
case link.UpdatePublicShareRequest_Update_TYPE_PASSWORD:
165182
if req.Update.GetGrant().Password == "" {
166183
// Remove the password
167-
res = m.db.Model(&publiclink).Update("password", "")
184+
res = m.db.Model(&publiclink).
185+
Where("id = ?", publiclink.Id).
186+
Update("password", "")
168187
} else {
169188
// Update the password
170189
hashedPwd, err := hashPassword(req.Update.GetGrant().Password, m.c.LinkPasswordHashCost)
171190
if err != nil {
172191
return nil, errors.Wrap(err, "could not hash share password")
173192
}
174-
res = m.db.Model(&publiclink).Update("password", hashedPwd)
193+
res = m.db.Model(&publiclink).
194+
Where("id = ?", publiclink.Id).
195+
Update("password", hashedPwd)
175196
}
176197
case link.UpdatePublicShareRequest_Update_TYPE_DESCRIPTION:
177-
res = m.db.Model(&publiclink).Update("description", req.Update.GetDescription())
198+
res = m.db.Model(&publiclink).
199+
Where("id = ?", publiclink.Id).
200+
Update("description", req.Update.GetDescription())
178201
case link.UpdatePublicShareRequest_Update_TYPE_NOTIFYUPLOADS:
179-
res = m.db.Model(&publiclink).Update("notify_uploads", req.Update.GetNotifyUploads())
202+
res = m.db.Model(&publiclink).
203+
Where("id = ?", publiclink.Id).
204+
Update("notify_uploads", req.Update.GetNotifyUploads())
180205
case link.UpdatePublicShareRequest_Update_TYPE_NOTIFYUPLOADSEXTRARECIPIENTS:
181-
res = m.db.Model(&publiclink).Update("notify_uploads_extra_recipients", req.Update.GetNotifyUploadsExtraRecipients())
206+
res = m.db.Model(&publiclink).
207+
Where("id = ?", publiclink.Id).
208+
Update("notify_uploads_extra_recipients", req.Update.GetNotifyUploadsExtraRecipients())
182209
default:
183210
return nil, fmt.Errorf("invalid update type: %v", req.GetUpdate().GetType())
184211
}
@@ -249,7 +276,7 @@ func (m *publicShareMgr) RevokePublicShare(ctx context.Context, u *user.User, re
249276
if err != nil {
250277
return err
251278
}
252-
res := m.db.Delete(&publiclink)
279+
res := m.db.Where("id = ?", publiclink.Id).Delete(&publiclink)
253280
return res.Error
254281

255282
}
@@ -284,7 +311,7 @@ func (m *publicShareMgr) GetPublicShareByToken(ctx context.Context, token string
284311
// Get Link by ID. Does not return orphans or expired links.
285312
func (m *publicShareMgr) getLinkByID(ctx context.Context, id *link.PublicShareId) (*model.PublicLink, error) {
286313
var link model.PublicLink
287-
res := m.db.First(&link, id.OpaqueId)
314+
res := m.db.Where("id = ?", id.OpaqueId).First(&link)
288315

289316
if res.RowsAffected == 0 || link.Orphan || isExpired(link) {
290317
return nil, errtypes.NotFound(id.OpaqueId)
@@ -375,8 +402,8 @@ func emptyLinkWithId(id string) (*model.PublicLink, error) {
375402
}
376403
share := &model.PublicLink{
377404
ProtoShare: model.ProtoShare{
378-
Model: gorm.Model{
379-
ID: uint(intId),
405+
BaseModel: model.BaseModel{
406+
Id: uint(intId),
380407
},
381408
},
382409
}

share/sql/share.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ func NewShareManager(ctx context.Context, m map[string]interface{}) (revashare.M
7575
}
7676

7777
// Migrate schemas
78-
err = db.AutoMigrate(&model.Share{}, &model.ShareState{})
79-
78+
err = db.AutoMigrate(&model.ShareID{}, &model.Share{}, &model.ShareState{})
8079
if err != nil {
8180
return nil, err
8281
}
@@ -125,6 +124,18 @@ func (m *shareMgr) Share(ctx context.Context, md *provider.ResourceInfo, g *coll
125124
ShareWith: shareWith,
126125
SharedWithIsGroup: g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP,
127126
}
127+
128+
// Create Shared ID
129+
id, err := createID(m.db)
130+
if err != nil {
131+
return nil, errors.Wrap(err, "failed to create id for PublicShare")
132+
}
133+
134+
share.BaseModel = model.BaseModel{
135+
Id: id,
136+
ShareId: model.ShareID{ID: id},
137+
}
138+
128139
share.UIDOwner = conversions.FormatUserID(md.Owner)
129140
share.UIDInitiator = conversions.FormatUserID(user.Id)
130141
share.InitialPath = md.Path
@@ -146,7 +157,7 @@ func (m *shareMgr) Share(ctx context.Context, md *provider.ResourceInfo, g *coll
146157
// Get Share by ID. Does not return orphans.
147158
func (m *shareMgr) getShareByID(ctx context.Context, id *collaboration.ShareId) (*model.Share, error) {
148159
var share model.Share
149-
res := m.db.First(&share, id.OpaqueId)
160+
res := m.db.Where("id = ?", id.OpaqueId).First(&share)
150161

151162
if res.RowsAffected == 0 || share.Orphan {
152163
return nil, errtypes.NotFound(id.OpaqueId)
@@ -243,7 +254,7 @@ func (m *shareMgr) Unshare(ctx context.Context, ref *collaboration.ShareReferenc
243254
if err != nil {
244255
return err
245256
}
246-
res := m.db.Delete(&share)
257+
res := m.db.Where("id = ?", share.Id).Delete(&share)
247258
return res.Error
248259
}
249260

@@ -260,7 +271,7 @@ func (m *shareMgr) UpdateShare(ctx context.Context, ref *collaboration.ShareRefe
260271
}
261272

262273
permissions := conversions.SharePermToInt(p.Permissions)
263-
res := m.db.Model(&share).Update("permissions", uint8(permissions))
274+
res := m.db.Model(&share).Where("id = ?", share.Id).Update("permissions", uint8(permissions))
264275
if res.Error != nil {
265276
return nil, res.Error
266277
}
@@ -390,7 +401,7 @@ func (m *shareMgr) ListReceivedShares(ctx context.Context, filters []*collaborat
390401
func (m *shareMgr) getShareState(ctx context.Context, share *model.Share, user *userpb.User) (*model.ShareState, error) {
391402
var shareState model.ShareState
392403
query := m.db.Model(&shareState).
393-
Where("share_id = ?", share.ID).
404+
Where("share_id = ?", share.Id).
394405
Where("user = ?", user.Username)
395406

396407
res := query.First(&shareState)
@@ -415,8 +426,8 @@ func emptyShareWithId(id string) (*model.Share, error) {
415426
}
416427
share := &model.Share{
417428
ProtoShare: model.ProtoShare{
418-
Model: gorm.Model{
419-
ID: uint(intId),
429+
BaseModel: model.BaseModel{
430+
Id: uint(intId),
420431
},
421432
},
422433
}

0 commit comments

Comments
 (0)