mirror of https://github.com/lukechilds/docs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
5.2 KiB
120 lines
5.2 KiB
5 years ago
|
---
|
||
5 years ago
|
|
||
|
|
||
5 years ago
|
---
|
||
5 years ago
|
# Collaborate with groups
|
||
|
{:.no_toc}
|
||
5 years ago
|
|
||
5 years ago
|
A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same among most applications. Radiks supplies interfaces for collaboration, making it easy to build private, collaborative apps.
|
||
5 years ago
|
|
||
5 years ago
|
You use the <a href="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts" target="_blank"><code>UserGroup</code></a> class to build a collaborative group with Radiks. In this section, you learn about this class.
|
||
5 years ago
|
|
||
5 years ago
|
* TOC
|
||
|
{:toc}
|
||
5 years ago
|
|
||
5 years ago
|
## Understand the UserGroup workflow
|
||
5 years ago
|
|
||
5 years ago
|
The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can subclass `UserGroup` with different attributes as needed.
|
||
5 years ago
|
|
||
|
The general workflow for creating a collaborative group that can share and edit encrypted models is as follows:
|
||
|
|
||
5 years ago
|
1. The admin of the group creates a new `UserGroup`.
|
||
|
This group acts as the 'hub' and controls the logic around inviting and removing users.
|
||
5 years ago
|
2. The admin invites one or more other users to a group:
|
||
5 years ago
|
* The admin specifies the username of the user they want to invite
|
||
|
* Radiks looks up the user's public key
|
||
|
* Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup`
|
||
|
3. When the invited user 'activates' an invitation, they create a `GroupMembership`.
|
||
|
They use this membership instance to reference information (such as private keys and signing keys) related to the group.
|
||
5 years ago
|
|
||
5 years ago
|
As they participate in a group, the group's members can create and update models that are related to the group. These models **must** contain a `userGroupId` attribute used to reference the group. This allows Radiks to know which keys to use for encryption and signing.
|
||
5 years ago
|
|
||
5 years ago
|
When needed, the group admin can remove a user from a group. To remove a user from the group, the admin creates a new private key for signing and encryption. Then, the admin updates the `GroupMembership` of all users _except_ the user they just removed. This update-and-remove action is also known as rotating the key.
|
||
5 years ago
|
|
||
|
After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key.
|
||
|
|
||
|
## Work with a UserGroup
|
||
|
|
||
|
This section details the methods on the <a href="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts" target="_blank"><code>UserGroup</code> class you can use to create, add members to, and query a group.
|
||
|
|
||
|
### Create a UserGroup
|
||
|
|
||
5 years ago
|
To create a `UserGroup`, you must import the class into your application from `radiks`:
|
||
5 years ago
|
|
||
|
```javascript
|
||
5 years ago
|
import { UserGroup } from 'radiks';
|
||
|
|
||
|
// ...
|
||
5 years ago
|
```
|
||
5 years ago
|
|
||
5 years ago
|
Calling `create` on a new `UserGroup` will create the group and activate an invitation for the group's creator.
|
||
|
|
||
|
|
||
|
```javascript
|
||
5 years ago
|
const group = new UserGroup({ name: 'My Group Name' });
|
||
|
await group.create();
|
||
5 years ago
|
```
|
||
|
|
||
|
A group's creator is also the group's admin.
|
||
5 years ago
|
|
||
|
|
||
5 years ago
|
### Invite users to become members
|
||
5 years ago
|
|
||
5 years ago
|
Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the user's `username`.
|
||
5 years ago
|
|
||
5 years ago
|
```javascript
|
||
5 years ago
|
import { UserGroup } from 'radiks';
|
||
|
|
||
|
const group = await UserGroup.findById(myGroupId);
|
||
|
const usernameToInvite = 'hankstoever.id';
|
||
|
const invitation = await group.makeGroupMembership(usernameToInvite);
|
||
|
console.log(invitation._id); // the ID used to later activate an invitation
|
||
5 years ago
|
```
|
||
5 years ago
|
|
||
5 years ago
|
#### Generic invitation
|
||
|
|
||
|
You can also create a generic invitation that any user can activate, if they are provided with randomly generated secret key, which should be used to decrypt the invitation. The key is generated when the generic invitation is being created.
|
||
|
|
||
|
~~~javascript
|
||
|
import { GenericGroupInvitation, UserGroup } from 'radiks';
|
||
|
const group = await UserGroup.findById(myGroupId);
|
||
|
// Creating generic invitation
|
||
|
const genericInvitation = await GenericGroupInvitation.makeGenericInvitation(group);
|
||
|
console.log(genericInvitation._id); // the ID used to later activate an invitation
|
||
|
console.log(genericInvitation.secretCode); // the secretCode used to later activate an invitation
|
||
|
~~~
|
||
|
|
||
|
|
||
5 years ago
|
### Accept an invitation
|
||
5 years ago
|
|
||
5 years ago
|
Use the `activate` method on a `GroupInvitation` instance to activate an invitation on behalf of a user:
|
||
5 years ago
|
|
||
5 years ago
|
```javascript
|
||
5 years ago
|
import { GroupInvitation, GenericGroupInvitation } from 'radiks';
|
||
5 years ago
|
|
||
5 years ago
|
// For user-specific invitation
|
||
5 years ago
|
const invitation = await GroupInvitation.findById(myInvitationID);
|
||
|
await invitation.activate();
|
||
5 years ago
|
|
||
|
// For generic invitation
|
||
|
const genericInvitation = await GenericGroupInvitation.findById(myInvitationID);
|
||
|
await genericInvitation.activate(mySecretCode);```
|
||
5 years ago
|
|
||
5 years ago
|
## View all activated UserGroups for the current user
|
||
5 years ago
|
|
||
|
Call `UserGroup.myGroups` to fetch all groups that the current user is a member of:
|
||
|
|
||
5 years ago
|
```javascript
|
||
5 years ago
|
import { UserGroup } from 'radiks';
|
||
|
|
||
|
const groups = await UserGroup.myGroups();
|
||
5 years ago
|
```
|
||
5 years ago
|
|
||
5 years ago
|
## Find a UserGroup
|
||
5 years ago
|
|
||
|
Use the method `UserGroup.find(id)` when fetching a specific UserGroup. This method has extra boilerplate to handle decrypting the model, because the private keys may need to be fetched from different models.
|
||
|
|
||
5 years ago
|
```javascript
|
||
5 years ago
|
const group = await UserGroup.find('my-id-here');
|
||
5 years ago
|
```
|