Skip to content

Commit

Permalink
Multi tenant docs (#540)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbarbugli authored Apr 26, 2024
2 parents 6df748d + 3f5a08d commit 0a3653c
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 20 deletions.
4 changes: 1 addition & 3 deletions docusaurus/video/docusaurus/docs/api/_common_/rtmp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ resp = call.get()
# user ID of an existing user
user_id = 'jane'
client.upsert_users(
users={
user_id: {'id': user_id}
}
UserRequest(id=user_id)
)

# the token will be valid for 1 hour
Expand Down
17 changes: 5 additions & 12 deletions docusaurus/video/docusaurus/docs/api/basics/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,9 @@ await client.upsertUsers({
<TabItem value="py" label="Python">

```py
users = {}
user = UserRequest(
id='user_id', role="user", custom={"color": "red"}, name="This is a test user",image= "link/to/profile/image",
client.upsert_users(UserRequest(
id='user_id', role="user", custom={"color": "red"}, name="This is a test user",image= "link/to/profile/image",)
)
users[user.id] = user
client.upsert_users(users=users)
```

</TabItem>
Expand Down Expand Up @@ -95,25 +92,21 @@ client.updateUsersPartial({
<TabItem value="py" label="Python">

```py
users = {}
user = UserRequest(
client.upsert_users(UserRequest(
id= 'userid',
role= 'user',
custom= {
"color": 'red',
},
name= 'This is a test user',
image= 'link/to/profile/image',
)

users[user.id] = user
client.upsert_users(users=users)
))

# or
client.update_users_partial(
users= [
{
id: user.id,
id: 'userid',
set: {
color: 'blue',
},
Expand Down
7 changes: 2 additions & 5 deletions docusaurus/video/docusaurus/docs/api/basics/get_started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,9 @@ client.createToken(userId, exp);
<TabItem value="py" label="Python">

```py
users = {}
user = UserRequest(
id="john", role="user", custom={"color": "red"}, name="John",image= "link/to/profile/image",
client.upsert_users(UserRequest(
id="john", role="user", custom={"color": "red"}, name="John",image= "link/to/profile/image",)
)
users[user.id] = user
client.upsert_users(users=users)

# the token will be valid for 1 hour
exp = int(time.time()) + 60 * 60
Expand Down
219 changes: 219 additions & 0 deletions docusaurus/video/docusaurus/docs/api/basics/multi-tenant.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
id: multi-tenant
sidebar_position: 4
slug: /multi-tenant
title: Multi-Tenant & Teams
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

Stream Video can be configured for teams/multi-tenant operation, allowing users to be organized into distinct, isolated teams. This configuration is essential for applications like Zoom or Disqus, where it's crucial that users within one team remain completely separated from others to ensure privacy and security.

## Teams

Stream Video has the concept of teams for users and calls. The purpose of teams is to provide a simple way to separate different groups of users and calls within a single application.

If a user belongs to a team, the API will ensure that such user will only be able to connect to calls from the same team. Features such as user search are limited so that a user can only search for users from the same team by default.

When enabling multi-tenant mode all user requests will always ensure that the request applies to a team the user belongs to. For instance, if a user from team "blue" tries to delete a message that was created on a channel from team "red" the API will return an error. If user doesn't have team set, it will only have access to users and channels that don't have team.

## Enable Teams for your application

In order to use Teams, your application must have multi-tenant mode enabled. You can enable multi-tenant from the dashboard (Overview screen) or using the API.

<Tabs groupId="examples">
<TabItem value="js" label="JavaScript">

```js
// shows the current status
const appSettings = await client.getAppSettings();
console.log(appSettings.app.multi_tenant_enabled);

// enables teams
client.updateAppSettings({
multi_tenant_enabled: true,
});
```

</TabItem>
<TabItem value="py" label="Python">

```py
# shows the current status
print(client.get_app().data.app.multi_tenant_enabled)

# enables teams
client.update_app(multi_tenant_enabled=True)
```

</TabItem>
</Tabs>

Please keep in mind that enabling/disabling multi-tenant changes permission checking for your users. Do not change this on a production app without testing that your integration supports it correctly.

## User teams

When multi-tenant is enabled, users can only be created from your back-end. This is necessary to ensure that a user cannot pick its own team/tenant.

<Tabs groupId="examples">
<TabItem value="js" label="JavaScript">

```js
client.upsertUsers({
users: {
['<user id>']: {
id: '<user id>',
name: 'Sara',
teams: ['red', 'blue'],
},
},
});
```

</TabItem>
<TabItem value="py" label="Python">

```py
client.upsert_users(
UserRequest(id=user_id, teams=["red", "blue"])
)
```

</TabItem>
</Tabs>

A user can be a member of up to 250 teams, a team name can be up to 100 characters. There is **no limit** to how many teams your application can have.

## Call team

Calls can be associated with a team. Users can create calls client-side but if their user is part of a team, they will have to specify a team or the request will be rejected with an error.

<Tabs groupId="examples">
<TabItem value="js" label="JavaScript">

```js
client.call('default', 'red-team-weekly').create({
data: { team: 'red', created_by_id: '<user_id>' },
});
```

</TabItem>
<TabItem value="py" label="Python">

```py
call = client.video.call("default", call_id)

response = call.create(
data=CallRequest(
created_by_id=user_id,
team="blue",
)
)
```

</TabItem>
</Tabs>

Call teams allow you to ensure proper permission checking for a multi tenant application. Keep in mind that you will still need to enforce that call IDs are unique.
There are two common way to address this: generate random IDs using a random UUID, prefix the team name to the id of the call.

## User search

By default client-side user search will only return results from teams that authenticated user is a part of. API injects filter `{team: {$in: [<user_teams>]}}` for every request that doesn't already contain filter for teams field. If you want to query users from all teams, you have to provide empty filter like this: `{teams:{}}`.

For server-side requests, this filter does not apply and you can search as usual and also filter by teams.

<Tabs groupId="examples">
<TabItem value="js" label="JavaScript">

```js
// search for users by name and team
client.queryUsers({
filter_conditions: {
name: 'Nick',
teams: { $in: ['red', 'blue'] },
},
});

// search for users that are not part of any team
client.queryUsers({
filter_conditions: {
name: 'Tom',
teams: null,
},
});
```

</TabItem>
<TabItem value="py" label="Python">

```py
# search for users by name and team
response = client.query_users(
QueryUsersPayload(
filter_conditions={
"name": {"$eq": "Nick"},
"teams": {"$in": ["red", "blue"]},
}
)
)

# search for users that are not part of any team
response = client.query_users(
QueryUsersPayload(
filter_conditions={
"teams": None,
}
)
)
```

</TabItem>
</Tabs>

## Query Calls

When using multi-tenant, the client-side query calls endpoint will only return calls that match the query and are on the same team as the authenticated user. The API injects filter `{team: {$in: [<user_teams>]}}` for every request that doesn't already contain filter for team field. If you want to query channels from all teams, you have to provide empty filter like this: `{team:{}}`.

For server-side requests, this filter does not apply and you can search as usual and also filter by teams.

<Tabs groupId="examples">
<TabItem value="js" label="JavaScript">

```js
// All calls
client.video.queryCalls();

// Calls without team
client.video.queryCalls({
filter_conditions: {
team: null,
},
});

// Calls with specific team
client.video.queryCalls({
filter_conditions: {
team: 'blue',
},
});
```

</TabItem>
<TabItem value="py" label="Python">

```py
# query calls by team field
response = client.video.query_calls(
filter_conditions={"id": call_id, "team": {"$eq": "blue"}}
)

# retrieve calls without a team
response = client.video.query_calls(
filter_conditions={"id": call_id, "team": {"$eq": "blue"}}
)
```

</TabItem>
</Tabs>

0 comments on commit 0a3653c

Please sign in to comment.