stack/packages/stack-shared/src/interface/serverInterface.ts
Stan Wohlwend 9ff2cff5c5
Organizations & RBAC (#22)
* added organizations route

* added orgganizations/[orgId] and orgganizations/[orgId]/users routes

* added more routes

* restructured dashboard pages, added organization page

* fixed redirect bug

* updated sidebar

* added mock orgnizations

* fixed breadcrumbs

* added edit org modal

* added memeber table

* added permission table

* Orgs & perms backend

* Fix build errors

* updated permission ui

* org -> team for frontend

* added enable team UI

* Stack App for teams

* formatted schema

* renamed all orgs to teams

* fixed docusaurus bug

* disabled docusaurus build progress bar

* added member profile and direct permission to profile

* removed dead code, memberProfile -> member

* removed teams attribute from the user object

* added /teams endpoint

* added create team endpoint

* added add-user endpoint

* moved add-user

* added server side get teams

* updated schema formatting

* added team enabled endpoints

* fixed type error

* moved get current teams to current-user/teams

* improved interface

* added create team

* hooked up team to frontend

* added hooked up team name with team member page

* added list team users

* added useUsers to team object

* fixed list team user bug

* added team update

* added list permissions and use permission on app

* added create permission

* list permission

* added permission list

* added inherited permission list

* add edit permission model

* restructured permission graph

* updated style

* added delete permission

* fixed delete permission bug

* added inheritence update

* fixed await bug

* fixed selection bug

* added permission update

* fixed update bug

* fixed team update refresh

* added remove user from team

* restructured permission and permission definition

* updated permission definition structure

* updated list permissions and grant permissions

* fixed list user permissions

* added grant permission

* fixed list permission

* added direct option to list team use permission

* fixed bugs

* fixed bugs

* added revoke permission

* inherited from -> contains

* added client list permission

* restructured stack-app teams and permissions

* fixed server teams and permissions

* fixed bug

* fixed bugs

* added teams pages to demo

* added styled component compiler

* added list teams

* added join and leave teams

* fixed prisma onDelete

* fixed type bugs

* removed on permission change for now

* added member

* fixed user and server user

* fixed imports

* added create team

* added more content to demo

* fixed recursion bug

* fixed recursion stack out of bound bug

* removed teamsEnabled

* added create team on sign-up options

* added create team on signup

* queriable -> queryable, fixed migration file

* fixed migration file

* fixed demo build error

* fixed license accidental change

* fixed tab styling

* added required to create permission id

* added more ui error hints

* fixed seed wrong setting

* default team name

* improved permission list ui

* improved demo display

* Update README.md

---------

Co-authored-by: Zai Shi <zaishi00@outlook.com>
2024-05-08 12:43:56 +02:00

325 lines
8.8 KiB
TypeScript

import {
ClientInterfaceOptions,
UserJson,
TokenStore,
StackClientInterface,
ReadonlyTokenStore,
OrglikeJson,
UserUpdateJson,
PermissionDefinitionJson,
PermissionDefinitionScopeJson as PermissionDefinitionScopeJson,
TeamMemberJson,
} from "./clientInterface";
import { Result } from "../utils/results";
import { ReadonlyJson } from "../utils/json";
export type ServerUserJson = UserJson & {
readonly serverMetadata: ReadonlyJson,
};
export type ServerUserUpdateJson = UserUpdateJson & {
readonly serverMetadata?: ReadonlyJson,
readonly primaryEmail?: string | null,
readonly primaryEmailVerified?: boolean,
}
export type ServerOrglikeCustomizableJson = Pick<ServerOrglikeJson, "displayName">;
export type ServerOrglikeJson = OrglikeJson & {};
export type ServerTeamCustomizableJson = ServerOrglikeCustomizableJson;
export type ServerTeamJson = ServerOrglikeJson;
export type ServerTeamMemberJson = TeamMemberJson
export type ServerPermissionDefinitionCustomizableJson = {
readonly id: string,
readonly description?: string,
readonly scope: PermissionDefinitionScopeJson,
readonly containPermissionIds: string[],
};
export type ServerPermissionDefinitionJson = PermissionDefinitionJson & ServerPermissionDefinitionCustomizableJson & {
readonly __databaseUniqueId: string,
readonly scope: PermissionDefinitionScopeJson,
};
export type ServerAuthApplicationOptions = (
& ClientInterfaceOptions
& (
| {
readonly secretServerKey: string,
}
| {
readonly projectOwnerTokens: ReadonlyTokenStore,
}
)
);
export class StackServerInterface extends StackClientInterface {
constructor(public override options: ServerAuthApplicationOptions) {
super(options);
}
protected async sendServerRequest(path: string, options: RequestInit, tokenStore: TokenStore | null, requestType: "server" | "admin" = "server") {
return await this.sendClientRequest(
path,
{
...options,
headers: {
"x-stack-secret-server-key": "secretServerKey" in this.options ? this.options.secretServerKey : "",
...options.headers,
},
},
tokenStore,
requestType,
);
}
async getServerUserByToken(tokenStore: TokenStore): Promise<Result<ServerUserJson>> {
const response = await this.sendServerRequest(
"/current-user?server=true",
{},
tokenStore,
);
const user: ServerUserJson | null = await response.json();
if (!user) return Result.error(new Error("Failed to get user"));
return Result.ok(user);
}
async getServerUserById(userId: string): Promise<Result<ServerUserJson>> {
const response = await this.sendServerRequest(
`/users/${userId}?server=true`,
{},
null,
);
const user: ServerUserJson | null = await response.json();
if (!user) return Result.error(new Error("Failed to get user"));
return Result.ok(user);
}
async listServerUserTeamPermissions(
options: {
teamId: string,
type: 'global' | 'team',
direct: boolean,
},
tokenStore: TokenStore
): Promise<ServerPermissionDefinitionJson[]> {
const response = await this.sendServerRequest(
`/current-user/teams/${options.teamId}/permissions?type=${options.type}&direct=${options.direct}&server=true`,
{},
tokenStore,
);
const permissions: ServerPermissionDefinitionJson[] = await response.json();
return permissions;
}
async listServerUserTeams(tokenStore: TokenStore): Promise<ServerTeamJson[]> {
const response = await this.sendServerRequest(
"/current-user/teams?server=true",
{},
tokenStore,
);
const teams: ServerTeamJson[] = await response.json();
return teams;
}
async listPermissionDefinitions(): Promise<ServerPermissionDefinitionJson[]> {
const response = await this.sendServerRequest(`/permission-definitions?server=true`, {}, null);
return await response.json();
}
async createPermissionDefinition(data: ServerPermissionDefinitionCustomizableJson): Promise<ServerPermissionDefinitionJson> {
const response = await this.sendServerRequest(
"/permission-definitions?server=true",
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
...data,
scope: {
type: "any-team",
}
}),
},
null,
);
return await response.json();
}
async updatePermissionDefinition(permissionId: string, data: Partial<ServerPermissionDefinitionCustomizableJson>): Promise<void> {
await this.sendServerRequest(
`/permission-definitions/${permissionId}?server=true`,
{
method: "PUT",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
null,
);
}
async deletePermissionDefinition(permissionId: string): Promise<void> {
await this.sendServerRequest(
`/permission-definitions/${permissionId}?server=true`,
{ method: "DELETE" },
null,
);
}
async listUsers(): Promise<ServerUserJson[]> {
const response = await this.sendServerRequest("/users?server=true", {}, null);
return await response.json();
}
async listTeams(): Promise<ServerTeamJson[]> {
const response = await this.sendServerRequest("/teams?server=true", {}, null);
return await response.json();
}
async listTeamMembers(teamId: string): Promise<ServerTeamMemberJson[]> {
const response = await this.sendServerRequest(`/teams/${teamId}/users?server=true`, {}, null);
return await response.json();
}
async createTeam(data: ServerTeamCustomizableJson): Promise<ServerTeamJson> {
const response = await this.sendServerRequest(
"/teams?server=true",
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
null,
);
return await response.json();
}
async updateTeam(teamId: string, data: Partial<ServerTeamCustomizableJson>): Promise<void> {
await this.sendServerRequest(
`/teams/${teamId}?server=true`,
{
method: "PUT",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
null,
);
}
async addUserToTeam(options: {
userId: string,
teamId: string,
}) {
await this.sendServerRequest(
`/teams/${options.teamId}/users/${options.userId}?server=true`,
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({}),
},
null,
);
}
async removeUserFromTeam(options: {
userId: string,
teamId: string,
}) {
await this.sendServerRequest(
`/teams/${options.teamId}/users/${options.userId}?server=true`,
{
method: "DELETE",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({}),
},
null,
);
}
async setServerUserCustomizableData(userId: string, update: ServerUserUpdateJson) {
await this.sendServerRequest(
`/users/${userId}?server=true`,
{
method: "PUT",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(update),
},
null,
);
}
async listTeamMemberPermissions(
options: {
teamId: string,
userId: string,
type: 'global' | 'team',
direct: boolean,
}
): Promise<ServerPermissionDefinitionJson[]> {
const response = await this.sendServerRequest(
`/teams/${options.teamId}/users/${options.userId}/permissions?server=true&type=${options.type}&direct=${options.direct}`,
{},
null
);
return await response.json();
}
async grantTeamUserPermission(teamId: string, userId: string, permissionId: string, type: 'global' | 'team') {
await this.sendServerRequest(
`/teams/${teamId}/users/${userId}/permissions/${permissionId}?server=true`,
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ type }),
},
null,
);
}
async revokeTeamUserPermission(teamId: string, userId: string, permissionId: string, type: 'global' | 'team') {
await this.sendServerRequest(
`/teams/${teamId}/users/${userId}/permissions/${permissionId}?server=true`,
{
method: "DELETE",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ type }),
},
null,
);
}
async deleteServerUser(userId: string) {
await this.sendServerRequest(
`/users/${userId}?server=true`,
{
method: "DELETE",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({}),
},
null,
);
}
}