From 1bcb06179b6e3cabdb1a9e39bf409b93c2f6a95c Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Thu, 9 Jan 2025 17:27:19 +0100 Subject: [PATCH 1/4] Allow to patch password/expires_at for permissions --- docs/permissions.md | 30 +++++++++++++++++++++++++++- web/permissions/permissions.go | 36 +++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/docs/permissions.md b/docs/permissions.md index 682a4597454..366efe56021 100644 --- a/docs/permissions.md +++ b/docs/permissions.md @@ -57,7 +57,7 @@ Some known types: - `io.cozy.jobs` and `io.cozy.triggers`, for [jobs](jobs.md) - `io.cozy.oauth.clients`, to list and revoke [OAuth 2 clients](auth.md) -It is also possible to use a wildcard to use a doctype and its sub-doctypes if +It is also possible to use a wildcard to use a doctype and its sub-doctypes if the doctype contains at least 3 `.`. For example, `io.cozy.bank.*` will give access to `io.cozy.bank`, `io.cozy.bank.accounts`, `io.cozy.bank.accounts.stats`, @@ -444,6 +444,34 @@ Accept: application/vnd.api+json } ``` +#### Request to update the password and the expiration date of the sharing link + +```http +PATCH /permissions/a340d5e0-d647-11e6-b66c-5fc9ce1e17c6 HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ +Content-Type: application/vnd.api+json +Accept: application/vnd.api+json +``` + +```json +{ + "data": { + "id": "a340d5e0-d647-11e6-b66c-5fc9ce1e17c6", + "type": "io.cozy.permissions", + "attributes": { + "password": "NewPassword", + "expires_at": "2025-01-01T00:00:00Z" + }, + "cozyMetadata": { + "doctypeVersion": 1, + "metadataVersion": 1, + "updatedAt": "2019-05-14T12:00:37.372193145+02:00" + } + } +} +``` + #### Request to add permissions ```http diff --git a/web/permissions/permissions.go b/web/permissions/permissions.go index 33c01cf1e47..fe3ab2852be 100644 --- a/web/permissions/permissions.go +++ b/web/permissions/permissions.go @@ -357,19 +357,36 @@ func patchPermission(getPerms getPermsFunc, paramName string) echo.HandlerFunc { patchSet := patch.Permissions != nil && len(patch.Permissions) > 0 patchCodes := len(patch.Codes) > 0 - if patchCodes == patchSet { - return ErrPatchCodeOrSet - } - toPatch, err := getPerms(instance, c.Param(paramName)) if err != nil { return err } - if patchCodes { - if !current.CanUpdateShareByLink(toPatch) { - return permission.ErrNotParent + if !patchSet && !current.CanUpdateShareByLink(toPatch) { + return permission.ErrNotParent + } + + if patchCodes == patchSet { + if patchSet { + return ErrPatchCodeOrSet + } + if patch.Password == nil && patch.ExpiresAt == nil { + return ErrPatchCodeOrSet } + } + + if pass, _ := patch.Password.(string); pass != "" { + hash, err := crypto.GenerateFromPassphrase([]byte(pass)) + if err != nil { + return err + } + toPatch.Password = hash + } + if patch.ExpiresAt != nil { + toPatch.ExpiresAt = patch.ExpiresAt + } + + if patchCodes { toPatch.PatchCodes(patch.Codes) } @@ -406,6 +423,11 @@ func patchPermission(getPerms getPermsFunc, paramName string) echo.HandlerFunc { return err } + // Don't send the password hash to the client + if toPatch.Password != nil { + toPatch.Password = true + } + return jsonapi.Data(c, http.StatusOK, &APIPermission{toPatch, nil}, nil) } } From a9a2022272fbad94258ea655b36fd502308a2243 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Mon, 13 Jan 2025 17:49:31 +0100 Subject: [PATCH 2/4] Allow to remove expires_at/password for a permission --- docs/permissions.md | 31 +++++++++++++++++++++++++++++++ model/permission/permissions.go | 9 +++++++-- web/permissions/permissions.go | 26 +++++++++++++++++++------- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/docs/permissions.md b/docs/permissions.md index 366efe56021..dfc9b27f55e 100644 --- a/docs/permissions.md +++ b/docs/permissions.md @@ -415,6 +415,9 @@ give the contacts application the permissions to use it. This route also accepts a [document metadata](https://github.com/cozy/cozy-doctypes/#document-metadata) to update document informations. +Giving an empty string for `password` or `expires_at` will remove it (while +omitting the field will keep the old value). + #### Request to add / remove codes with a document metadata ```http @@ -472,6 +475,34 @@ Accept: application/vnd.api+json } ``` +#### Request to remove the password and the expiration date of the sharing link + +```http +PATCH /permissions/a340d5e0-d647-11e6-b66c-5fc9ce1e17c6 HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ +Content-Type: application/vnd.api+json +Accept: application/vnd.api+json +``` + +```json +{ + "data": { + "id": "a340d5e0-d647-11e6-b66c-5fc9ce1e17c6", + "type": "io.cozy.permissions", + "attributes": { + "password": "", + "expires_at": "" + }, + "cozyMetadata": { + "doctypeVersion": 1, + "metadataVersion": 1, + "updatedAt": "2019-05-14T12:00:37.372193145+02:00" + } + } +} +``` + #### Request to add permissions ```http diff --git a/model/permission/permissions.go b/model/permission/permissions.go index 499b893f965..6f58b2d857a 100644 --- a/model/permission/permissions.go +++ b/model/permission/permissions.go @@ -31,7 +31,7 @@ type Permission struct { Type string `json:"type,omitempty"` SourceID string `json:"source_id,omitempty"` Permissions Set `json:"permissions,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ExpiresAt interface{} `json:"expires_at,omitempty"` Codes map[string]string `json:"codes,omitempty"` ShortCodes map[string]string `json:"shortcodes,omitempty"` Password interface{} `json:"password,omitempty"` @@ -113,7 +113,12 @@ func (p *Permission) Expired() bool { if p.ExpiresAt == nil { return false } - return p.ExpiresAt.Before(time.Now()) + if expiresAt, _ := p.ExpiresAt.(string); expiresAt != "" { + if at, err := time.Parse(time.RFC3339, expiresAt); err == nil { + return at.Before(time.Now()) + } + } + return true } // AddRules add some rules to the permission doc diff --git a/web/permissions/permissions.go b/web/permissions/permissions.go index fe3ab2852be..6282b7992f4 100644 --- a/web/permissions/permissions.go +++ b/web/permissions/permissions.go @@ -375,15 +375,27 @@ func patchPermission(getPerms getPermsFunc, paramName string) echo.HandlerFunc { } } - if pass, _ := patch.Password.(string); pass != "" { - hash, err := crypto.GenerateFromPassphrase([]byte(pass)) - if err != nil { - return err + if pass, ok := patch.Password.(string); ok { + if pass == "" { + toPatch.Password = nil + } else { + hash, err := crypto.GenerateFromPassphrase([]byte(pass)) + if err != nil { + return err + } + toPatch.Password = hash } - toPatch.Password = hash } - if patch.ExpiresAt != nil { - toPatch.ExpiresAt = patch.ExpiresAt + if at, ok := patch.ExpiresAt.(string); ok { + if patch.ExpiresAt == "" { + toPatch.ExpiresAt = nil + } else { + expiresAt, err := time.Parse(time.RFC3339, at) + if err != nil { + return jsonapi.InvalidAttribute("expires_at", err) + } + toPatch.ExpiresAt = expiresAt + } } if patchCodes { From b5c32e3acf01e01ee75f9854b2e16f2f2ce5fa74 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Wed, 15 Jan 2025 16:20:42 +0100 Subject: [PATCH 3/4] Allow to create a permission with expires_at --- web/permissions/permissions.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/web/permissions/permissions.go b/web/permissions/permissions.go index 6282b7992f4..23b9985cff0 100644 --- a/web/permissions/permissions.go +++ b/web/permissions/permissions.go @@ -145,6 +145,15 @@ func createPermission(c echo.Context) error { tiny = false } } + } else { + tiny = false + if at, ok := subdoc.ExpiresAt.(string); ok { + expires, err := time.Parse(time.RFC3339, at) + if err != nil { + return jsonapi.InvalidAttribute("expires_at", err) + } + expiresAt = &expires + } } var codes map[string]string From 64f57e310423ab3c981476ff0caeae4c2be32d92 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Tue, 21 Jan 2025 09:36:43 +0100 Subject: [PATCH 4/4] Fix expires_at: null in CouchDB This issue was discovered by @Merkur39. When creating an io.cozy.permissions for a share by link, the expires_at field was set to null in CouchDB, even if the field was declared as omitempty. A nil pointer to a time.Time is not considered by Go as an empty value for a field of the type interface{}. So, we need to use the empty value of type interface{} instead to fix the JSON marshaling. --- model/permission/permissions.go | 9 ++++++++- web/permissions/permissions.go | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/model/permission/permissions.go b/model/permission/permissions.go index 6f58b2d857a..2501bd67ccf 100644 --- a/model/permission/permissions.go +++ b/model/permission/permissions.go @@ -500,7 +500,14 @@ func checkSetPermissions(set Set, parent *Permission) error { } // CreateShareSet creates a Permission doc for sharing by link -func CreateShareSet(db prefixer.Prefixer, parent *Permission, sourceID string, codes, shortcodes map[string]string, subdoc Permission, expiresAt *time.Time) (*Permission, error) { +func CreateShareSet( + db prefixer.Prefixer, + parent *Permission, + sourceID string, + codes, shortcodes map[string]string, + subdoc Permission, + expiresAt interface{}, +) (*Permission, error) { set := subdoc.Permissions if err := checkSetPermissions(set, parent); err != nil { return nil, err diff --git a/web/permissions/permissions.go b/web/permissions/permissions.go index 23b9985cff0..c6b4e81b8e5 100644 --- a/web/permissions/permissions.go +++ b/web/permissions/permissions.go @@ -135,7 +135,7 @@ func createPermission(c echo.Context) error { return err } - var expiresAt *time.Time + var expiresAt interface{} if ttl != "" { if d, errd := bigduration.ParseDuration(ttl); errd == nil { ex := time.Now().Add(d)