Collection Sharing

note

In the future we will have more ways to share collections among users (e.g: PAKE), but until we do, the only way to sure is using public-key cryptography which is what this section is about.

This section assumes you have already read the collections section and are familiar with collections.

Collections can be shared among users for easy collaboration. This can be used for sharing files between users, editing a shared document, or giving others access to parts of your account (e.g. a calendar).

Getting your public key

An important part of using public key cryptography is making sure the other side of the interaction is whoever you think it is. This can be achieved by verifying each-other's public key. Your public key, as the name implies, is public, so feel free to share it with others.

Getting your public key:

const etebase = await Etebase.Account.login("username", "password");
const invitationManager = etebase.getInvitationManager();
// You can now share your pubkey
const myPubkey = collectionInvitationManager.pubkey;

Pretty-print the public key

You would almost always want users to compare public keys in a secure manner (usually phone, in-person, or secure chat). The following function formats the pubkey for you in a way that's easy for users to compare, so you don't have to figure it out yourself.

// The delimiter parameter is optional and defaults to " "
const delimiter = " ";
const prettyFingerprint = Etebase.getPrettyFingerprint(myPubkey, delimiter);
console.log(prettyFingerprint);
/* Output:
45680 71497 88570 93128
19189 84243 25687 20837
47924 46071 54113 18789
*/

The format we use is similar to the one used by Signal. Please refer to their post for more information about it.

Sending invitations

The first step towards sharing collections with other users is sending them an invitation. Only a collection admin can send an invitation, so make sure you are one before trying to invite users.

const invitationManager = etebase.getInvitationManager();
// Fetch their public key
const user2 = await invitationManager.fetchUserProfile("username2");
// Verify user2.pubkey is indeed the pubkey you expect.
// This is done in a secure channel (e.g. encrypted chat or in person)
// Assuming the pubkey is as expected, send the invitation
await invitationManager.invite(collection, "username2", user2.pubkey,
Etebase.CollectionAccessLevel.ReadOnly);

As you can see from the example above, when inviting a user you also set the wanted access level. Allowed values are: Admin, ReadWrite, and ReadOnly.

Responding to invitations

In the previous example we sent an access invitation to user2. In this section we will see how to respond to these invitations.

// login as user2
const etebase = await Etebase.Account.login("username2", "password");
const invitationManager = etebase.getInvitationManager();
// List pending invitations
const invitations = await invitationManager.listIncoming();
const invitation = invitations.data[0]; // get first invitation
// Verify the sender's pubkey is indeed the pubkey you expect.
// This is done in a secure channel (e.g. encrypted chat or in person)
const fromPubkey = invitation.fromPubkey;
// We can now either accept
await invitationManager.accept(invitation);
// or reject the invitation:
await invitationManager.reject(invitation);

Leaving collections

Sometimes a user may want to leave a collection he has been invited to. This can be done as follows:

const memberManager = collectionManager.getMemberManager(collection);
await memberManager.leave();

Controlling access

Like with invitations, only collection admins can see who has access to a collection, modify access levels and revoke access altogether.

Listing collection members

const memberManager = collectionManager.getMemberManager(collection);
const members = await memberManager.list();
// Print the users and their access levels
for (const member of members.data) {
console.log(member.username, member.accessLevel);
}

Modifying access level

const memberManager = collectionManager.getMemberManager(collection);
const newAccessLevel = Etebase.CollectionAccessLevel.ReadWrite;
await memberManager.modifyAccessLevel("username2", newAccessLevel);

Revoking access

const memberManager = collectionManager.getMemberManager(collection);
await memberManager.remove("username2");

Iterating through responses

Like with the rest of the Etebase API, list responses can be iterated through in smaller chunks. Here is how it's done for outgoing invitations, but the same can be done for incoming invitations and collection members.

const invitationManager = etebase.getInvitationManager();
let iterator = null;
while (true) {
const items = await invitationManager.listOutgoing({ iterator, limit: 30 });
iterator = items.iterator;
processOutgoingInvitations(items.data);
if (items.done) {
break;
}
}