Skip to content

Commit e96cc8f

Browse files
authored
Implement shared ID for public links and shares (#20)
A new table share_ids is added to the database, which contains the sequence of IDs that are shared between public links and shares. This is required to comply with Web's requirement of having unique IDs across the different share types.
1 parent d4c5866 commit e96cc8f

File tree

4 files changed

+109
-35
lines changed

4 files changed

+109
-35
lines changed

share/model.go

+33-12
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 the Web UI 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;not null"`
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"`
40-
UIDInitiator string `gorm:"size:64"`
41-
ItemType ItemType `gorm:"size:16;index:"` // file | folder | reference | symlink
62+
UIDInitiator string `gorm:"size:64;index"`
63+
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)