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

Allow to share with a group #4331

Merged
merged 22 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion cmd/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ cozy-stack instances add allows to create an instance on the cozy for a
given domain.

If the COZY_DISABLE_INSTANCES_ADD_RM env variable is set, creating and
destroying instances will be desactivated and the content of this variable will
destroying instances will be disabled and the content of this variable will
be used as the error message.
`,
Example: "$ cozy-stack instances add --passphrase cozy --apps drive,photos,settings,home,store cozy.localhost:8080",
Expand Down
3 changes: 2 additions & 1 deletion cozy.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ jobs:
# - "push": sending push notifications
# - "sms": sending SMS notifications
# - "sendmail": sending mails
# - "share-replicate": for cozy to cozy sharing
# - "share-group": for cozy to cozy sharing
# - "share-replicate": idem
# - "share-track": idem
# - "share-upload": idem
# - "thumbnail": creatings and deleting thumbnails for images
Expand Down
2 changes: 1 addition & 1 deletion docs/cli/cozy-stack_instances_add.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ cozy-stack instances add allows to create an instance on the cozy for a
given domain.

If the COZY_DISABLE_INSTANCES_ADD_RM env variable is set, creating and
destroying instances will be desactivated and the content of this variable will
destroying instances will be disabled and the content of this variable will
be used as the error message.


Expand Down
43 changes: 29 additions & 14 deletions docs/sharing-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,20 +473,35 @@ care of it later.
### Description of a sharing

- An identifier (the same for all members of the sharing)
- A list of `members`. The first one is the owner. For each member, we have
the URL of the cozy, a contact name, a public name, an email, a status, a
read-only flag, and some credentials to authorize the transfer of data
between the owner and the recipients. The status can be:
- `owner` for the member that has created the sharing
- `mail-not-sent` for a member that has been added, but its invitation
has not yet been sent (often, this status is used only for a few
seconds)
- `pending` for a member with an invitation sent, but who has not clicked
on the link
- `seen` for a member that has clicked on the invitation link, but has not
setup the Cozy to Cozy replication for the sharing
- `ready` for a member where the Cozy to Cozy replication has been set up
- `revoked` for a member who is on longer in the sharing
- A list of `members`. The first one is the owner. For each member, we have:
- `status`, a status that can be:
- `owner` for the member that has created the sharing
- `mail-not-sent` for a member that has been added, but its
invitation has not yet been sent (often, this status is used only
for a few seconds)
- `pending` for a member with an invitation sent, but who has not
clicked on the link
- `seen` for a member that has clicked on the invitation link, but
has not setup the Cozy to Cozy replication for the sharing
- `ready` for a member where the Cozy to Cozy replication has been
set up
- `revoked` for a member who is on longer in the sharing
- `name`, a contact name
- `public_name`, a public name
- `email`, the email address
- `instance`, the URL of the Cozy
- `read_only`, a flag to tell if the contact is restricted to read-only mode
- `only_in_groups`, a flag that will be false if the member has been added
as a single contact
- `groups`, a list of indexes of the groups
- A list of `groups`, with for each one:
- `id`, the identifier of the io.cozy.contacts.groups
- `name`, the name of the group
- `addedBy`, the index of the member that has added the group
- `read_only`, a flag to tell if the group is restricted to read-only mode
- `revoked`, a flag set to true when the group is revoked from the sharing
- Some `credentials` to authorize the transfer of data between the owner and
the recipients
- A `description` (one sentence that will help people understand what is
shared and why)
- A flag `active` that says if the sharing is currently active for at least
Expand Down
131 changes: 121 additions & 10 deletions docs/sharing.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ Content-Type: application/vnd.api+json
{
"id": "2a31ce0128b5f89e40fd90da3f014087",
"type": "io.cozy.contacts"
},
{
"id": "51bbc980acb0013cb5f618c04daba326",
"type": "io.cozy.contacts.groups"
}
]
}
Expand Down Expand Up @@ -136,6 +140,20 @@ Content-Type: application/vnd.api+json
"status": "mail-not-sent",
"name": "Bob",
"email": "[email protected]"
},
{
"status": "mail-not-sent",
"name": "Gaby",
"email": "[email protected]",
"only_in_groups": true,
"groups": [0]
nono marked this conversation as resolved.
Show resolved Hide resolved
}
],
"groups": [
{
"id": "51bbc980acb0013cb5f618c04daba326",
"name": "G. people",
"addedBy": 0
}
],
"rules": [
Expand Down Expand Up @@ -747,9 +765,9 @@ HTTP/1.1 204 No Content

### POST /sharings/:sharing-id/recipients

This route allows the sharer to add new recipients to a sharing. It can also be
used by a recipient when the sharing has `open_sharing` set to true if the
recipient doesn't have the `read_only` flag
This route allows the sharer to add new recipients (and groups of recipients)
to a sharing. It can also be used by a recipient when the sharing has
`open_sharing` set to true if the recipient doesn't have the `read_only` flag.

#### Request

Expand Down Expand Up @@ -868,9 +886,9 @@ Content-Type: application/vnd.api+json
### POST /sharings/:sharing-id/recipients/delegated

This is an internal route for the stack. It is called by the recipient cozy on
the owner cozy to add recipients to the sharing (`open_sharing: true` only). It
should send an `email` address, but if the email address is not known, an
`instance` URL can also be used.
the owner cozy to add recipients and groups to the sharing (`open_sharing:
true` only). Data for direct recipients should contain an email address but if
it is not known, an instance URL can also be provided.

#### Request

Expand All @@ -892,6 +910,15 @@ Content-Type: application/vnd.api+json
"email": "[email protected]"
}
]
},
"groups": {
"data": [
{
"id": "b57cd790b2f4013c3ced18c04daba326",
"name": "Dance",
"addedBy": 1
}
]
}
}
}
Expand All @@ -911,12 +938,68 @@ Content-Type: application/json
}
```

### POST /sharings/:sharing-id/members/:index/invitation

This is an internal route for the stack. It is called by the recipient cozy on
the owner cozy to send an invitation.

#### Request

```http
POST /sharings/ce8835a061d0ef68947afe69a0046722/members/4/invitation HTTP/1.1
Host: alice.example.net
Content-Type: application/vnd.api+json
```

```json
{
"data": {
"type": "io.cozy.sharings.members",
"attributes": {
"email": "[email protected]"
}
}
}
```

#### Response

```http
HTTP/1.1 200 OK
Content-Type: application/json
```

```json
{
"[email protected]": "uS6wN7fTYaLZ-GdC_P6UWA"
}
```

### DELETE /sharings/:sharing-id/groups/:group-index/:member-index

This is an internal route for the stack. It is called by the recipient cozy on
the owner cozy to remove a member of a sharing from a group.

#### Request

```http
DELETE /sharings/ce8835a061d0ef68947afe69a0046722/groups/0/1 HTTP/1.1
Host: alice.example.net
```

#### Response

```http
HTTP/1.1 204 No Content
```

### PUT /sharings/:sharing-id/recipients

This internal route is used to update the list of members, their states, emails
and names, on the recipients cozy. The token used for this route can be the
access token for a sharing where synchronization is active, or the sharecode
for a member who has only a shortcut to the sharing on their Cozy instance.
This internal route is used to update the list of members (their states, emails
and names) and the list of groups on the recipients cozy. The token used for
this route can be the access token for a sharing where synchronization is
active, or the sharecode for a member who has only a shortcut to the sharing on
their Cozy instance.

#### Request

Expand Down Expand Up @@ -953,6 +1036,13 @@ Content-Type: application/vnd.api+json
"email": "[email protected]",
"read_only": true
}
],
"included": [
{
"id": "51bbc980acb0013cb5f618c04daba326",
nono marked this conversation as resolved.
Show resolved Hide resolved
"name": "G. people",
"addedBy": 0
}
]
}
```
Expand Down Expand Up @@ -1137,6 +1227,27 @@ Host: alice.example.net
HTTP/1.1 204 No Content
```

### DELETE /sharings/:sharing-id/groups/:index

This route can be only be called on the cozy instance of the sharer to revoke a
group of the sharing. The parameter is the index of this recipient in the
`groups` array of the sharing. The `removed` property for this group will be
set to `true`, and it will revoke the members of this group unless they are
still part of the sharing via another group or as direct recipients.

#### Request

```http
DELETE /sharings/ce8835a061d0ef68947afe69a0046722/groups/0 HTTP/1.1
Host: alice.example.net
```

#### Response

```http
HTTP/1.1 204 No Content
```

### DELETE /sharings/:sharing-id/recipients/self

This route can be used by an application in the cozy of a recipient to remove it
Expand Down
15 changes: 11 additions & 4 deletions docs/workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,18 @@ in the config file, via the `fs.auto_clean_trashed_after` parameter.

## share workers

The stack have 3 workers to power the sharings (internal usage only):
The stack have 4 workers to power the sharings (internal usage only):

1. `share-track`, to update the `io.cozy.shared` database
2. `share-replicate`, to start a replicator for most documents
3. `share-upload`, to upload files
1. `share-group`, to add/remove members to a sharing
nono marked this conversation as resolved.
Show resolved Hide resolved
2. `share-track`, to update the `io.cozy.shared` database
3. `share-replicate`, to start a replicator for most documents
4. `share-upload`, to upload files

### Share-group

When a contact is added to or removed from a group, the change should be
reflected in the group's sharings' recipients. The message is composed of the
contact ID, the list of groups added and the list of groups removed.

### Share-track

Expand Down
83 changes: 83 additions & 0 deletions model/contact/contact_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package contact

import (
"encoding/json"
"fmt"
"testing"

"github.com/cozy/cozy-stack/pkg/config/config"
"github.com/cozy/cozy-stack/pkg/consts"
"github.com/cozy/cozy-stack/pkg/couchdb"
"github.com/cozy/cozy-stack/pkg/prefixer"
"github.com/gofrs/uuid/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGetAllContacts(t *testing.T) {
config.UseTestFile(t)
instPrefix := prefixer.NewPrefixer(0, "cozy-test", "cozy-test")
t.Cleanup(func() { _ = couchdb.DeleteDB(instPrefix, consts.Contacts) })

g := NewGroup()
g.SetID(uuid.Must(uuid.NewV7()).String())

gaby := fmt.Sprintf(`{
"address": [],
"birthday": "",
"birthplace": "",
"company": "",
"cozy": [],
"cozyMetadata": {
"createdAt": "2024-02-13T15:05:58.917Z",
"createdByApp": "Contacts",
"createdByAppVersion": "1.7.0",
"doctypeVersion": 3,
"metadataVersion": 1,
"updatedAt": "2024-02-13T15:06:21.046Z",
"updatedByApps": [
{
"date": "2024-02-13T15:06:21.046Z",
"slug": "Contacts",
"version": "1.7.0"
}
]
},
"displayName": "Gaby",
"email": [],
"fullname": "Gaby",
"gender": "female",
"indexes": {
"byFamilyNameGivenNameEmailCozyUrl": "gaby"
},
"jobTitle": "",
"metadata": {
"cozy": true,
"version": 1
},
"name": {
"givenName": "Gaby"
},
"note": "",
"phone": [],
"relationships": {
"groups": {
"data": [
{
"_id": "%s",
"_type": "io.cozy.contacts.groups"
}
]
}
}
}`, g.ID())

doc := couchdb.JSONDoc{Type: consts.Contacts, M: make(map[string]interface{})}
require.NoError(t, json.Unmarshal([]byte(gaby), &doc.M))
require.NoError(t, couchdb.CreateDoc(instPrefix, &doc))

contacts, err := g.GetAllContacts(instPrefix)
require.NoError(t, err)
require.Len(t, contacts, 1)
assert.Equal(t, contacts[0].PrimaryName(), "Gaby")
}
Loading
Loading