collection.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. package controller
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/ente-io/museum/pkg/repo/cast"
  7. "runtime/debug"
  8. "strings"
  9. "github.com/ente-io/museum/pkg/controller/access"
  10. "github.com/gin-contrib/requestid"
  11. "github.com/google/go-cmp/cmp"
  12. "github.com/google/go-cmp/cmp/cmpopts"
  13. "github.com/ente-io/museum/pkg/utils/array"
  14. "github.com/ente-io/museum/pkg/utils/auth"
  15. "github.com/gin-gonic/gin"
  16. "github.com/ente-io/museum/ente"
  17. "github.com/ente-io/museum/pkg/repo"
  18. "github.com/ente-io/museum/pkg/utils/time"
  19. "github.com/ente-io/stacktrace"
  20. log "github.com/sirupsen/logrus"
  21. )
  22. const (
  23. CollectionDiffLimit = 2500
  24. )
  25. // CollectionController encapsulates logic that deals with collections
  26. type CollectionController struct {
  27. PublicCollectionCtrl *PublicCollectionController
  28. AccessCtrl access.Controller
  29. BillingCtrl *BillingController
  30. CollectionRepo *repo.CollectionRepository
  31. UserRepo *repo.UserRepository
  32. FileRepo *repo.FileRepository
  33. QueueRepo *repo.QueueRepository
  34. CastRepo *cast.Repository
  35. TaskRepo *repo.TaskLockRepository
  36. }
  37. // Create creates a collection
  38. func (c *CollectionController) Create(collection ente.Collection, ownerID int64) (ente.Collection, error) {
  39. // The key attribute check is to ensure that user does not end up uploading any files before actually setting the key attributes.
  40. if _, keyErr := c.UserRepo.GetKeyAttributes(ownerID); keyErr != nil {
  41. return ente.Collection{}, stacktrace.Propagate(keyErr, "Unable to get keyAttributes")
  42. }
  43. collectionType := collection.Type
  44. collection.Owner.ID = ownerID
  45. collection.UpdationTime = time.Microseconds()
  46. // [20th Dec 2022] Patch on server side untill majority of the existing mobile clients upgrade to a version higher > 0.7.0
  47. // https://github.com/ente-io/photos-app/pull/725
  48. if collection.Type == "CollectionType.album" {
  49. collection.Type = "album"
  50. }
  51. if !array.StringInList(collection.Type, ente.ValidCollectionTypes) {
  52. return ente.Collection{}, stacktrace.Propagate(fmt.Errorf("unexpected collection type %s", collection.Type), "")
  53. }
  54. collection, err := c.CollectionRepo.Create(collection)
  55. if err != nil {
  56. if err == ente.ErrUncategorizeCollectionAlreadyExists || err == ente.ErrFavoriteCollectionAlreadyExist {
  57. dbCollection, err := c.CollectionRepo.GetCollectionByType(ownerID, collectionType)
  58. if err != nil {
  59. return ente.Collection{}, stacktrace.Propagate(err, "")
  60. }
  61. if dbCollection.IsDeleted {
  62. return ente.Collection{}, stacktrace.Propagate(fmt.Errorf("special collection of type : %s is deleted", collectionType), "")
  63. }
  64. return dbCollection, nil
  65. }
  66. return ente.Collection{}, stacktrace.Propagate(err, "")
  67. }
  68. return collection, nil
  69. }
  70. // GetOwned returns the list of collections owned by a user
  71. func (c *CollectionController) GetOwned(userID int64, sinceTime int64, app ente.App) ([]ente.Collection, error) {
  72. collections, err := c.CollectionRepo.GetCollectionsOwnedByUser(userID, sinceTime, app)
  73. if err != nil {
  74. return nil, stacktrace.Propagate(err, "")
  75. }
  76. go func() {
  77. defer func() {
  78. if r := recover(); r != nil {
  79. log.Errorf("Panic caught: %s, stack: %s", r, string(debug.Stack()))
  80. }
  81. }()
  82. collectionsV2, errV2 := c.CollectionRepo.GetCollectionsOwnedByUserV2(userID, sinceTime, app)
  83. if errV2 != nil {
  84. log.WithError(errV2).Error("failed to fetch collections using v2")
  85. }
  86. isEqual := cmp.Equal(collections, collectionsV2, cmpopts.SortSlices(func(a, b ente.Collection) bool { return a.ID < b.ID }))
  87. if !isEqual {
  88. jsonV1, _ := json.Marshal(collections)
  89. jsonV2, _ := json.Marshal(collectionsV2)
  90. log.WithFields(log.Fields{
  91. "v1": string(jsonV1),
  92. "v2": string(jsonV2),
  93. }).Error("collections diff didn't match")
  94. } else {
  95. log.Info("collections diff matched")
  96. }
  97. }()
  98. return collections, nil
  99. }
  100. // GetOwnedV2 returns the list of collections owned by a user using optimized query
  101. func (c *CollectionController) GetOwnedV2(userID int64, sinceTime int64, app ente.App) ([]ente.Collection, error) {
  102. collections, err := c.CollectionRepo.GetCollectionsOwnedByUserV2(userID, sinceTime, app)
  103. if err != nil {
  104. return nil, stacktrace.Propagate(err, "")
  105. }
  106. return collections, nil
  107. }
  108. // GetCollection returns the collection for given collectionID
  109. func (c *CollectionController) GetCollection(ctx *gin.Context, userID int64, cID int64) (ente.Collection, error) {
  110. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  111. CollectionID: cID,
  112. ActorUserID: userID,
  113. IncludeDeleted: true,
  114. })
  115. if err != nil {
  116. return ente.Collection{}, stacktrace.Propagate(err, "")
  117. }
  118. return resp.Collection, nil
  119. }
  120. // GetSharedWith returns the list of collections that are shared with a user
  121. func (c *CollectionController) GetSharedWith(userID int64, sinceTime int64, app ente.App) ([]ente.Collection, error) {
  122. collections, err := c.CollectionRepo.GetCollectionsSharedWithUser(userID, sinceTime, app)
  123. if err != nil {
  124. return nil, stacktrace.Propagate(err, "")
  125. }
  126. return collections, nil
  127. }
  128. // Share shares a collection with a user
  129. func (c *CollectionController) Share(ctx *gin.Context, req ente.AlterShareRequest) ([]ente.CollectionUser, error) {
  130. fromUserID := auth.GetUserID(ctx.Request.Header)
  131. cID := req.CollectionID
  132. encryptedKey := req.EncryptedKey
  133. toUserEmail := strings.ToLower(strings.TrimSpace(req.Email))
  134. // default role type
  135. role := ente.VIEWER
  136. if req.Role != nil {
  137. role = *req.Role
  138. }
  139. toUserID, err := c.UserRepo.GetUserIDWithEmail(toUserEmail)
  140. if err != nil {
  141. return nil, stacktrace.Propagate(err, "")
  142. }
  143. if toUserID == fromUserID {
  144. return nil, stacktrace.Propagate(ente.ErrBadRequest, "Can not share collection with self")
  145. }
  146. collection, err := c.CollectionRepo.Get(cID)
  147. if err != nil {
  148. return nil, stacktrace.Propagate(err, "")
  149. }
  150. if !collection.AllowSharing() {
  151. return nil, stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("sharing %s is not allowed", collection.Type))
  152. }
  153. if fromUserID != collection.Owner.ID {
  154. return nil, stacktrace.Propagate(ente.ErrPermissionDenied, "")
  155. }
  156. err = c.BillingCtrl.HasActiveSelfOrFamilySubscription(fromUserID)
  157. if err != nil {
  158. return nil, stacktrace.Propagate(err, "")
  159. }
  160. err = c.CollectionRepo.Share(cID, fromUserID, toUserID, encryptedKey, role, time.Microseconds())
  161. if err != nil {
  162. return nil, stacktrace.Propagate(err, "")
  163. }
  164. sharees, err := c.GetSharees(ctx, cID, fromUserID)
  165. if err != nil {
  166. return nil, stacktrace.Propagate(err, "")
  167. }
  168. return sharees, nil
  169. }
  170. // UnShare unshares a collection with a user
  171. func (c *CollectionController) UnShare(ctx *gin.Context, cID int64, fromUserID int64, toUserEmail string) ([]ente.CollectionUser, error) {
  172. toUserID, err := c.UserRepo.GetUserIDWithEmail(toUserEmail)
  173. if err != nil {
  174. return nil, stacktrace.Propagate(ente.ErrNotFound, "")
  175. }
  176. collection, err := c.CollectionRepo.Get(cID)
  177. if err != nil {
  178. return nil, stacktrace.Propagate(err, "")
  179. }
  180. isLeavingCollection := toUserID == fromUserID
  181. if fromUserID != collection.Owner.ID || isLeavingCollection {
  182. return nil, stacktrace.Propagate(ente.ErrPermissionDenied, "")
  183. }
  184. err = c.CollectionRepo.UnShare(cID, toUserID)
  185. if err != nil {
  186. return nil, stacktrace.Propagate(err, "")
  187. }
  188. err = c.CastRepo.RevokeForGivenUserAndCollection(ctx, cID, toUserID)
  189. if err != nil {
  190. return nil, stacktrace.Propagate(err, "")
  191. }
  192. sharees, err := c.GetSharees(ctx, cID, fromUserID)
  193. if err != nil {
  194. return nil, stacktrace.Propagate(err, "")
  195. }
  196. return sharees, nil
  197. }
  198. // Leave leaves the collection owned by someone else,
  199. func (c *CollectionController) Leave(ctx *gin.Context, cID int64) error {
  200. userID := auth.GetUserID(ctx.Request.Header)
  201. collection, err := c.CollectionRepo.Get(cID)
  202. if err != nil {
  203. return stacktrace.Propagate(err, "")
  204. }
  205. if userID == collection.Owner.ID {
  206. return stacktrace.Propagate(ente.ErrPermissionDenied, "can not leave collection owned by self")
  207. }
  208. sharedCollectionIDs, err := c.CollectionRepo.GetCollectionIDsSharedWithUser(userID)
  209. if err != nil {
  210. return stacktrace.Propagate(err, "")
  211. }
  212. if !array.Int64InList(cID, sharedCollectionIDs) {
  213. return nil
  214. }
  215. err = c.CastRepo.RevokeForGivenUserAndCollection(ctx, cID, userID)
  216. if err != nil {
  217. return stacktrace.Propagate(err, "")
  218. }
  219. err = c.CollectionRepo.UnShare(cID, userID)
  220. if err != nil {
  221. return stacktrace.Propagate(err, "")
  222. }
  223. return nil
  224. }
  225. func (c *CollectionController) UpdateShareeMagicMetadata(ctx *gin.Context, req ente.UpdateCollectionMagicMetadata) error {
  226. actorUserId := auth.GetUserID(ctx.Request.Header)
  227. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  228. CollectionID: req.ID,
  229. ActorUserID: actorUserId,
  230. })
  231. if err != nil {
  232. return stacktrace.Propagate(err, "")
  233. }
  234. if resp.Collection.Owner.ID == actorUserId {
  235. return stacktrace.Propagate(ente.NewBadRequestWithMessage("owner can not update sharee magic metadata"), "")
  236. }
  237. err = c.CollectionRepo.UpdateShareeMetadata(req.ID, resp.Collection.Owner.ID, actorUserId, req.MagicMetadata, time.Microseconds())
  238. if err != nil {
  239. return stacktrace.Propagate(err, "failed to update sharee magic metadata")
  240. }
  241. return nil
  242. }
  243. // ShareURL generates a public auth-token for the given collectionID
  244. func (c *CollectionController) ShareURL(ctx context.Context, userID int64, req ente.CreatePublicAccessTokenRequest) (
  245. ente.PublicURL, error) {
  246. collection, err := c.CollectionRepo.Get(req.CollectionID)
  247. if err != nil {
  248. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  249. }
  250. if !collection.AllowSharing() {
  251. return ente.PublicURL{}, stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("sharing %s is not allowed", collection.Type))
  252. }
  253. if userID != collection.Owner.ID {
  254. return ente.PublicURL{}, stacktrace.Propagate(ente.ErrPermissionDenied, "")
  255. }
  256. err = c.BillingCtrl.HasActiveSelfOrFamilySubscription(userID)
  257. if err != nil {
  258. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  259. }
  260. response, err := c.PublicCollectionCtrl.CreateAccessToken(ctx, req)
  261. if err != nil {
  262. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  263. }
  264. return response, nil
  265. }
  266. // UpdateShareURL updates the shared url configuration
  267. func (c *CollectionController) UpdateShareURL(ctx context.Context, userID int64, req ente.UpdatePublicAccessTokenRequest) (
  268. ente.PublicURL, error) {
  269. if err := c.verifyOwnership(req.CollectionID, userID); err != nil {
  270. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  271. }
  272. err := c.BillingCtrl.HasActiveSelfOrFamilySubscription(userID)
  273. if err != nil {
  274. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  275. }
  276. response, err := c.PublicCollectionCtrl.UpdateSharedUrl(ctx, req)
  277. if err != nil {
  278. return ente.PublicURL{}, stacktrace.Propagate(err, "")
  279. }
  280. return response, nil
  281. }
  282. // DisableSharedURL disable a public auth-token for the given collectionID
  283. func (c *CollectionController) DisableSharedURL(ctx context.Context, userID int64, cID int64) error {
  284. if err := c.verifyOwnership(cID, userID); err != nil {
  285. return stacktrace.Propagate(err, "")
  286. }
  287. err := c.PublicCollectionCtrl.Disable(ctx, cID)
  288. return stacktrace.Propagate(err, "")
  289. }
  290. // AddFiles adds files to a collection
  291. func (c *CollectionController) AddFiles(ctx *gin.Context, userID int64, files []ente.CollectionFileItem, cID int64) error {
  292. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  293. CollectionID: cID,
  294. ActorUserID: userID,
  295. IncludeDeleted: false,
  296. })
  297. if err != nil {
  298. return stacktrace.Propagate(err, "failed to verify collection access")
  299. }
  300. if !resp.Role.CanAdd() {
  301. return stacktrace.Propagate(ente.ErrPermissionDenied, fmt.Sprintf("user %d with role %s can not add files", userID, *resp.Role))
  302. }
  303. collectionOwnerID := resp.Collection.Owner.ID
  304. filesOwnerID := userID
  305. // Verify that the user owns each file
  306. fileIDs := make([]int64, 0)
  307. for _, file := range files {
  308. fileIDs = append(fileIDs, file.ID)
  309. }
  310. err = c.AccessCtrl.VerifyFileOwnership(ctx, &access.VerifyFileOwnershipParams{
  311. ActorUserId: userID,
  312. FileIDs: fileIDs,
  313. })
  314. if err != nil {
  315. return stacktrace.Propagate(err, "Failed to verify fileOwnership")
  316. }
  317. err = c.CollectionRepo.AddFiles(cID, collectionOwnerID, files, filesOwnerID)
  318. if err != nil {
  319. return stacktrace.Propagate(err, "")
  320. }
  321. return nil
  322. }
  323. // RestoreFiles restore files from trash and add to the collection
  324. func (c *CollectionController) RestoreFiles(ctx *gin.Context, userID int64, cID int64, files []ente.CollectionFileItem) error {
  325. _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  326. CollectionID: cID,
  327. ActorUserID: userID,
  328. IncludeDeleted: false,
  329. VerifyOwner: true,
  330. })
  331. if err != nil {
  332. return stacktrace.Propagate(err, "failed to verify collection access")
  333. }
  334. // Verify that the user owns each file
  335. for _, file := range files {
  336. // todo #perf find owners of all files
  337. ownerID, err := c.FileRepo.GetOwnerID(file.ID)
  338. if err != nil {
  339. return stacktrace.Propagate(err, "")
  340. }
  341. if ownerID != userID {
  342. log.WithFields(log.Fields{
  343. "file_id": file.ID,
  344. "owner_id": ownerID,
  345. "user_id": userID,
  346. }).Error("invalid ops: can't add file which isn't owned by user")
  347. return stacktrace.Propagate(ente.ErrPermissionDenied, "")
  348. }
  349. }
  350. err = c.CollectionRepo.RestoreFiles(ctx, userID, cID, files)
  351. if err != nil {
  352. return stacktrace.Propagate(err, "")
  353. }
  354. return nil
  355. }
  356. // MoveFiles from one collection to another collection. Both the collections and files should belong to
  357. // single user
  358. func (c *CollectionController) MoveFiles(ctx *gin.Context, req ente.MoveFilesRequest) error {
  359. userID := auth.GetUserID(ctx.Request.Header)
  360. _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  361. CollectionID: req.FromCollectionID,
  362. ActorUserID: userID,
  363. IncludeDeleted: false,
  364. VerifyOwner: true,
  365. })
  366. if err != nil {
  367. return stacktrace.Propagate(err, "failed to verify if actor owns fromCollection")
  368. }
  369. _, err = c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  370. CollectionID: req.ToCollectionID,
  371. ActorUserID: userID,
  372. IncludeDeleted: false,
  373. VerifyOwner: true,
  374. })
  375. if err != nil {
  376. return stacktrace.Propagate(err, "failed to verify if actor owns toCollection")
  377. }
  378. // Verify that the user owns each file
  379. fileIDs := make([]int64, 0)
  380. for _, file := range req.Files {
  381. fileIDs = append(fileIDs, file.ID)
  382. }
  383. err = c.AccessCtrl.VerifyFileOwnership(ctx, &access.VerifyFileOwnershipParams{
  384. ActorUserId: userID,
  385. FileIDs: fileIDs,
  386. })
  387. if err != nil {
  388. stacktrace.Propagate(err, "Failed to verify fileOwnership")
  389. }
  390. err = c.CollectionRepo.MoveFiles(ctx, req.ToCollectionID, req.FromCollectionID, req.Files, userID, userID)
  391. return stacktrace.Propagate(err, "") // return nil if err is nil
  392. }
  393. // RemoveFilesV3 removes files from a collection as long as owner(s) of the file is different from collection owner
  394. func (c *CollectionController) RemoveFilesV3(ctx *gin.Context, req ente.RemoveFilesV3Request) error {
  395. actorUserID := auth.GetUserID(ctx.Request.Header)
  396. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  397. CollectionID: req.CollectionID,
  398. ActorUserID: actorUserID,
  399. VerifyOwner: false,
  400. })
  401. if err != nil {
  402. return stacktrace.Propagate(err, "failed to verify collection access")
  403. }
  404. err = c.isRemoveAllowed(ctx, actorUserID, resp.Collection.Owner.ID, req.FileIDs)
  405. if err != nil {
  406. return stacktrace.Propagate(err, "file removal check failed")
  407. }
  408. err = c.CollectionRepo.RemoveFilesV3(ctx, req.CollectionID, req.FileIDs)
  409. if err != nil {
  410. return stacktrace.Propagate(err, "failed to remove files")
  411. }
  412. return nil
  413. }
  414. // isRemoveAllowed verifies that given set of files can be removed from the collection or not
  415. func (c *CollectionController) isRemoveAllowed(ctx *gin.Context, actorUserID int64, collectionOwnerID int64, fileIDs []int64) error {
  416. ownerToFilesMap, err := c.FileRepo.GetOwnerToFileIDsMap(ctx, fileIDs)
  417. if err != nil {
  418. return stacktrace.Propagate(err, "failed to get owner to fileIDs map")
  419. }
  420. // verify that none of the file belongs to the collection owner
  421. if _, ok := ownerToFilesMap[collectionOwnerID]; ok {
  422. return ente.NewBadRequestWithMessage("can not remove files owned by album owner")
  423. }
  424. if collectionOwnerID != actorUserID {
  425. // verify that user is only trying to remove files owned by them
  426. if len(ownerToFilesMap) > 1 {
  427. return stacktrace.Propagate(ente.ErrPermissionDenied, "can not remove files owned by others")
  428. }
  429. // verify that user is only trying to remove files owned by them
  430. if _, ok := ownerToFilesMap[actorUserID]; !ok {
  431. return stacktrace.Propagate(ente.ErrPermissionDenied, "can not remove files owned by others")
  432. }
  433. }
  434. return nil
  435. }
  436. // GetDiffV2 returns the changes in user's collections since a timestamp, along with hasMore bool flag.
  437. func (c *CollectionController) GetDiffV2(ctx *gin.Context, cID int64, userID int64, sinceTime int64) ([]ente.File, bool, error) {
  438. reqContextLogger := log.WithFields(log.Fields{
  439. "user_id": userID,
  440. "collection_id": cID,
  441. "since_time": sinceTime,
  442. "req_id": requestid.Get(ctx),
  443. })
  444. _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  445. CollectionID: cID,
  446. ActorUserID: userID,
  447. })
  448. if err != nil {
  449. return nil, false, stacktrace.Propagate(err, "failed to verify access")
  450. }
  451. diff, hasMore, err := c.getDiff(cID, sinceTime, CollectionDiffLimit, reqContextLogger)
  452. if err != nil {
  453. return nil, false, stacktrace.Propagate(err, "")
  454. }
  455. // hide private metadata before returning files info in diff
  456. for idx := range diff {
  457. if diff[idx].OwnerID != userID {
  458. diff[idx].MagicMetadata = nil
  459. }
  460. }
  461. return diff, hasMore, nil
  462. }
  463. func (c *CollectionController) GetFile(ctx *gin.Context, collectionID int64, fileID int64) (*ente.File, error) {
  464. userID := auth.GetUserID(ctx.Request.Header)
  465. files, err := c.CollectionRepo.GetFile(collectionID, fileID)
  466. if err != nil {
  467. return nil, stacktrace.Propagate(err, "")
  468. }
  469. if len(files) == 0 {
  470. return nil, stacktrace.Propagate(&ente.ErrFileNotFoundInAlbum, "")
  471. }
  472. file := files[0]
  473. if file.OwnerID != userID {
  474. cIDs, err := c.CollectionRepo.GetCollectionIDsSharedWithUser(userID)
  475. if err != nil {
  476. return nil, stacktrace.Propagate(err, "")
  477. }
  478. if !array.Int64InList(collectionID, cIDs) {
  479. return nil, stacktrace.Propagate(ente.ErrPermissionDenied, "")
  480. }
  481. }
  482. if file.IsDeleted {
  483. return nil, stacktrace.Propagate(&ente.ErrFileNotFoundInAlbum, "")
  484. }
  485. return &file, nil
  486. }
  487. // GetPublicDiff returns the changes in the collections since a timestamp, along with hasMore bool flag.
  488. func (c *CollectionController) GetPublicDiff(ctx *gin.Context, sinceTime int64) ([]ente.File, bool, error) {
  489. accessContext := auth.MustGetPublicAccessContext(ctx)
  490. reqContextLogger := log.WithFields(log.Fields{
  491. "public_id": accessContext.ID,
  492. "collection_id": accessContext.CollectionID,
  493. "since_time": sinceTime,
  494. "req_id": requestid.Get(ctx),
  495. })
  496. diff, hasMore, err := c.getDiff(accessContext.CollectionID, sinceTime, CollectionDiffLimit, reqContextLogger)
  497. if err != nil {
  498. return nil, false, stacktrace.Propagate(err, "")
  499. }
  500. // hide private metadata before returning files info in diff
  501. for idx := range diff {
  502. if diff[idx].MagicMetadata != nil {
  503. diff[idx].MagicMetadata = nil
  504. }
  505. }
  506. return diff, hasMore, nil
  507. }
  508. // getDiff returns the diff in user's collection since a timestamp, along with hasMore bool flag.
  509. // The function will never return partial result for a version. To maintain this promise, it will not be able to honor
  510. // the limit parameter. Based on the db state, compared to the limit, the diff length can be
  511. // less (case 1), more (case 2), or same (case 3, 4)
  512. // Example: Assume we have 11 files with following versions: v0, v1, v1, v1, v1, v1, v1, v1, v2, v2, v2 (count = 7 v1, 3 v2)
  513. // client has synced up till version v0.
  514. // case 1: ( sinceTime: v0, limit = 8):
  515. // The method will discard the entries with version v2 and return only 7 entries with version v1.
  516. // case 2: (sinceTime: v0, limit 5):
  517. // Instead of returning 5 entries with version V1, method will return all 7 entries with version v1.
  518. // case 3: (sinceTime: v0, limit 7):
  519. // The method will return all 7 entries with version V1.
  520. // case 4: (sinceTime: v0, limit >=10):
  521. // The method will all 10 entries in the diff
  522. func (c *CollectionController) getDiff(cID int64, sinceTime int64, limit int, logger *log.Entry) ([]ente.File, bool, error) {
  523. // request for limit +1 files
  524. diffLimitPlusOne, err := c.CollectionRepo.GetDiff(cID, sinceTime, limit+1)
  525. if err != nil {
  526. return nil, false, stacktrace.Propagate(err, "")
  527. }
  528. if len(diffLimitPlusOne) <= limit {
  529. // case 4: all files changed after sinceTime are included.
  530. return diffLimitPlusOne, false, nil
  531. }
  532. lastFileVersion := diffLimitPlusOne[limit].UpdationTime
  533. filteredDiffs := c.removeFilesWithVersion(diffLimitPlusOne, lastFileVersion)
  534. filteredDiffLen := len(filteredDiffs)
  535. if filteredDiffLen > 0 { // case 1 or case 3
  536. if filteredDiffLen < limit {
  537. // logging case 1
  538. logger.
  539. WithField("last_file_version", lastFileVersion).
  540. WithField("filtered_diff_len", filteredDiffLen).
  541. Info(fmt.Sprintf("less than limit (%d) files in diff", limit))
  542. }
  543. return filteredDiffs, true, nil
  544. }
  545. // case 2
  546. diff, err := c.CollectionRepo.GetFilesWithVersion(cID, lastFileVersion)
  547. logger.
  548. WithField("last_file_version", lastFileVersion).
  549. WithField("count", len(diff)).
  550. Info(fmt.Sprintf("more than limit (%d) files with same version", limit))
  551. if err != nil {
  552. return nil, false, stacktrace.Propagate(err, "")
  553. }
  554. return diff, true, nil
  555. }
  556. // removeFilesWithVersion returns filtered list of files are removing all files with given version.
  557. // Important: The method assumes that files are sorted by increasing order of File.UpdationTime
  558. func (c *CollectionController) removeFilesWithVersion(files []ente.File, version int64) []ente.File {
  559. var i = len(files) - 1
  560. for ; i >= 0; i-- {
  561. if files[i].UpdationTime != version {
  562. // found index (from end) where file's version is different from given version
  563. break
  564. }
  565. }
  566. return files[0 : i+1]
  567. }
  568. // GetSharees returns the list of users a collection has been shared with
  569. func (c *CollectionController) GetSharees(ctx *gin.Context, cID int64, userID int64) ([]ente.CollectionUser, error) {
  570. _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  571. CollectionID: cID,
  572. ActorUserID: userID,
  573. })
  574. if err != nil {
  575. return nil, stacktrace.Propagate(err, "Access check failed")
  576. }
  577. sharees, err := c.CollectionRepo.GetSharees(cID)
  578. if err != nil {
  579. return nil, stacktrace.Propagate(err, "")
  580. }
  581. return sharees, nil
  582. }
  583. // Trash deletes a given collection and files exclusive to the collection
  584. func (c *CollectionController) Trash(ctx *gin.Context, userID int64, cID int64) error {
  585. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  586. CollectionID: cID,
  587. ActorUserID: userID,
  588. IncludeDeleted: true,
  589. VerifyOwner: true,
  590. })
  591. if err != nil {
  592. return stacktrace.Propagate(err, "")
  593. }
  594. if !resp.Collection.AllowDelete() {
  595. return stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("deleting albums of type %s is not allowed", resp.Collection.Type))
  596. }
  597. if resp.Collection.IsDeleted {
  598. log.WithFields(log.Fields{
  599. "c_id": cID,
  600. "user_id": userID,
  601. }).Warning("Collection is already deleted")
  602. return nil
  603. }
  604. err = c.PublicCollectionCtrl.Disable(ctx, cID)
  605. if err != nil {
  606. return stacktrace.Propagate(err, "failed to disabled public share url")
  607. }
  608. err = c.CollectionRepo.ScheduleDelete(cID, true)
  609. if err != nil {
  610. return stacktrace.Propagate(err, "")
  611. }
  612. return nil
  613. }
  614. // TrashV3 deletes a given collection and based on user input (TrashCollectionV3Request.KeepFiles as FALSE) , it will move all files present in the underlying collection
  615. // to trash.
  616. func (c *CollectionController) TrashV3(ctx *gin.Context, req ente.TrashCollectionV3Request) error {
  617. if req.KeepFiles == nil {
  618. return ente.ErrBadRequest
  619. }
  620. userID := auth.GetUserID(ctx.Request.Header)
  621. cID := req.CollectionID
  622. resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
  623. CollectionID: cID,
  624. ActorUserID: userID,
  625. IncludeDeleted: true,
  626. VerifyOwner: true,
  627. })
  628. if err != nil {
  629. return stacktrace.Propagate(err, "")
  630. }
  631. if !resp.Collection.AllowDelete() {
  632. return stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("deleting albums of type %s is not allowed", resp.Collection.Type))
  633. }
  634. if resp.Collection.IsDeleted {
  635. log.WithFields(log.Fields{
  636. "c_id": cID,
  637. "user_id": userID,
  638. }).Warning("Collection is already deleted")
  639. return nil
  640. }
  641. if *req.KeepFiles {
  642. // Verify that all files from this particular collections have been removed.
  643. count, err := c.CollectionRepo.GetCollectionsFilesCount(cID)
  644. if err != nil {
  645. return stacktrace.Propagate(err, "")
  646. }
  647. if count != 0 {
  648. return stacktrace.Propagate(&ente.ErrCollectionNotEmpty, fmt.Sprintf("Collection file count %d", count))
  649. }
  650. }
  651. err = c.PublicCollectionCtrl.Disable(ctx, cID)
  652. if err != nil {
  653. return stacktrace.Propagate(err, "failed to disabled public share url")
  654. }
  655. err = c.CastRepo.RevokeTokenForCollection(ctx, cID)
  656. if err != nil {
  657. return stacktrace.Propagate(err, "failed to revoke cast token")
  658. }
  659. // Continue with current delete flow till. This disables sharing for this collection and then queue it up for deletion
  660. err = c.CollectionRepo.ScheduleDelete(cID, false)
  661. if err != nil {
  662. return stacktrace.Propagate(err, "")
  663. }
  664. return nil
  665. }
  666. // Rename updates the collection's name
  667. func (c *CollectionController) Rename(userID int64, cID int64, encryptedName string, nameDecryptionNonce string) error {
  668. if err := c.verifyOwnership(cID, userID); err != nil {
  669. return stacktrace.Propagate(err, "")
  670. }
  671. err := c.CollectionRepo.Rename(cID, encryptedName, nameDecryptionNonce)
  672. if err != nil {
  673. return stacktrace.Propagate(err, "")
  674. }
  675. return nil
  676. }
  677. // UpdateMagicMetadata updates the magic metadata for given collection
  678. func (c *CollectionController) UpdateMagicMetadata(ctx *gin.Context, request ente.UpdateCollectionMagicMetadata, isPublicMetadata bool) error {
  679. userID := auth.GetUserID(ctx.Request.Header)
  680. if err := c.verifyOwnership(request.ID, userID); err != nil {
  681. return stacktrace.Propagate(err, "")
  682. }
  683. // todo: verify version mismatch later. We are not planning to resync collection on clients,
  684. // so ignore that check until then. Ideally, after file size info sync, we should enable
  685. err := c.CollectionRepo.UpdateMagicMetadata(ctx, request.ID, request.MagicMetadata, isPublicMetadata)
  686. if err != nil {
  687. return stacktrace.Propagate(err, "")
  688. }
  689. return nil
  690. }
  691. func (c *CollectionController) HandleAccountDeletion(ctx context.Context, userID int64, logger *log.Entry) error {
  692. logger.Info("disabling shared collections with or by the user")
  693. sharedCollections, err := c.CollectionRepo.GetAllSharedCollections(ctx, userID)
  694. if err != nil {
  695. return stacktrace.Propagate(err, "")
  696. }
  697. logger.Info(fmt.Sprintf("shared collections count: %d", len(sharedCollections)))
  698. for _, shareCollection := range sharedCollections {
  699. logger.WithField("shared_collection", shareCollection).Info("disable shared collection")
  700. err = c.CollectionRepo.UnShare(shareCollection.CollectionID, shareCollection.ToUserID)
  701. if err != nil {
  702. return stacktrace.Propagate(err, "")
  703. }
  704. }
  705. err = c.CastRepo.RevokeTokenForUser(ctx, userID)
  706. if err != nil {
  707. return stacktrace.Propagate(err, "failed to revoke cast token for user")
  708. }
  709. err = c.PublicCollectionCtrl.HandleAccountDeletion(ctx, userID, logger)
  710. return stacktrace.Propagate(err, "")
  711. }
  712. // Verify that user owns the collection
  713. func (c *CollectionController) verifyOwnership(cID int64, userID int64) error {
  714. collection, err := c.CollectionRepo.Get(cID)
  715. if err != nil {
  716. return stacktrace.Propagate(err, "")
  717. }
  718. if userID != collection.Owner.ID {
  719. return stacktrace.Propagate(ente.ErrPermissionDenied, "")
  720. }
  721. return nil
  722. }