55f91d2cdf
* fix(workspace): auto approval * fix(scopes): access scopes across the server * fix(hasAccessRole): establish for all mutations * feat(token): scoping does not require the token to exist * chore(scopes): added additional roles * fix: replaced UNAUTHORIZED_ACCESS_ERROR with UNAUTHORIZED * fix(email): user list scopes
467 lines
12 KiB
TypeScript
467 lines
12 KiB
TypeScript
import { flatMap } from '#lodash'
|
|
|
|
/**
|
|
* Speckle role constants
|
|
* - Stream - user roles in the context of a specific stream
|
|
* - Server - user roles in the context of the entire server
|
|
*/
|
|
export const Roles = Object.freeze(<const>{
|
|
Stream: {
|
|
Owner: 'stream:owner',
|
|
Contributor: 'stream:contributor',
|
|
Reviewer: 'stream:reviewer'
|
|
},
|
|
Workspace: {
|
|
Admin: 'workspace:admin',
|
|
Member: 'workspace:member',
|
|
Guest: 'workspace:guest'
|
|
},
|
|
Server: {
|
|
Admin: 'server:admin',
|
|
User: 'server:user',
|
|
Guest: 'server:guest',
|
|
ArchivedUser: 'server:archived-user'
|
|
}
|
|
})
|
|
|
|
export const RoleInfo = Object.freeze(<const>{
|
|
Stream: {
|
|
[Roles.Stream.Owner]: {
|
|
title: 'Owner',
|
|
description: 'Can edit project, including settings, collaborators and all models',
|
|
weight: 1000
|
|
},
|
|
[Roles.Stream.Contributor]: {
|
|
title: 'Can edit',
|
|
description:
|
|
'Can publish and load models from connectors and view and comment in the web viewer',
|
|
weight: 500
|
|
},
|
|
[Roles.Stream.Reviewer]: {
|
|
title: 'Can view',
|
|
description: 'Can view and comment on models in the web viewer',
|
|
weight: 100
|
|
}
|
|
},
|
|
Server: {
|
|
[Roles.Server.Admin]: {
|
|
title: 'Admin',
|
|
description: 'Can edit server, including settings, users and all projects',
|
|
weight: 1000
|
|
},
|
|
[Roles.Server.User]: {
|
|
title: 'User',
|
|
description: 'Can create and own projects',
|
|
weight: 100
|
|
},
|
|
[Roles.Server.Guest]: {
|
|
title: 'Guest',
|
|
description: "Can contribute to projects they're invited to",
|
|
weight: 50
|
|
},
|
|
[Roles.Server.ArchivedUser]: {
|
|
title: 'Archived',
|
|
description: 'Can no longer access server',
|
|
weight: 10
|
|
}
|
|
},
|
|
Workspace: {
|
|
[Roles.Workspace.Admin]: {
|
|
title: 'Admin',
|
|
description: 'Can edit workspace, including settings, members and all projects',
|
|
weight: 1000
|
|
},
|
|
[Roles.Workspace.Member]: {
|
|
title: 'Member',
|
|
description: 'Can create and own projects',
|
|
weight: 100
|
|
},
|
|
[Roles.Workspace.Guest]: {
|
|
title: 'Guest',
|
|
description: "Can contribute to projects they're invited to",
|
|
weight: 50
|
|
}
|
|
}
|
|
})
|
|
|
|
export type ServerRoles = (typeof Roles)['Server'][keyof (typeof Roles)['Server']]
|
|
export type WorkspaceRoles =
|
|
(typeof Roles)['Workspace'][keyof (typeof Roles)['Workspace']]
|
|
export type StreamRoles = (typeof Roles)['Stream'][keyof (typeof Roles)['Stream']]
|
|
|
|
export type AvailableRoles = ServerRoles | StreamRoles | WorkspaceRoles
|
|
|
|
/**
|
|
* Workspace seat type constants
|
|
*/
|
|
export const SeatTypes = Object.freeze(<const>{
|
|
Editor: 'editor',
|
|
Viewer: 'viewer'
|
|
})
|
|
|
|
export type WorkspaceSeatType = (typeof SeatTypes)[keyof typeof SeatTypes]
|
|
|
|
/**
|
|
* Speckle scope constants
|
|
* - Scopes define what kind of access has a user approved for a specific access token
|
|
*/
|
|
export const Scopes = Object.freeze(<const>{
|
|
Streams: {
|
|
Read: 'streams:read',
|
|
Write: 'streams:write'
|
|
},
|
|
Profile: {
|
|
Read: 'profile:read',
|
|
Write: 'profile:write',
|
|
Email: 'profile:email',
|
|
Delete: 'profile:delete'
|
|
},
|
|
Users: {
|
|
Read: 'users:read',
|
|
Email: 'users:email',
|
|
Invite: 'users:invite'
|
|
},
|
|
Server: {
|
|
Stats: 'server:stats',
|
|
Setup: 'server:setup'
|
|
},
|
|
Tokens: {
|
|
Read: 'tokens:read',
|
|
Write: 'tokens:write'
|
|
},
|
|
Apps: {
|
|
Read: 'apps:read',
|
|
Write: 'apps:write'
|
|
},
|
|
Automate: {
|
|
ReportResults: 'automate:report-results'
|
|
},
|
|
AutomateFunctions: {
|
|
Read: 'automate-functions:read',
|
|
Write: 'automate-functions:write'
|
|
},
|
|
Workspaces: {
|
|
Create: 'workspace:create',
|
|
Read: 'workspace:read',
|
|
Update: 'workspace:update',
|
|
Delete: 'workspace:delete'
|
|
},
|
|
Gatekeeper: {
|
|
WorkspaceBilling: 'workspace:billing'
|
|
}
|
|
})
|
|
|
|
export type StreamScopes = (typeof Scopes)['Streams'][keyof (typeof Scopes)['Streams']]
|
|
export type ProfileScopes = (typeof Scopes)['Profile'][keyof (typeof Scopes)['Profile']]
|
|
export type UserScopes = (typeof Scopes)['Users'][keyof (typeof Scopes)['Users']]
|
|
export type ServerScopes = (typeof Scopes)['Server'][keyof (typeof Scopes)['Server']]
|
|
export type TokenScopes = (typeof Scopes)['Tokens'][keyof (typeof Scopes)['Tokens']]
|
|
export type AppScopes = (typeof Scopes)['Apps'][keyof (typeof Scopes)['Apps']]
|
|
export type AutomateScopes =
|
|
(typeof Scopes)['Automate'][keyof (typeof Scopes)['Automate']]
|
|
export type AutomateFunctionScopes =
|
|
(typeof Scopes)['AutomateFunctions'][keyof (typeof Scopes)['AutomateFunctions']]
|
|
export type WorkspaceScopes =
|
|
(typeof Scopes)['Workspaces'][keyof (typeof Scopes)['Workspaces']]
|
|
export type GatekeeperScopes =
|
|
(typeof Scopes)['Gatekeeper'][keyof (typeof Scopes)['Gatekeeper']]
|
|
|
|
export type AvailableScopes =
|
|
| StreamScopes
|
|
| ProfileScopes
|
|
| UserScopes
|
|
| ServerScopes
|
|
| TokenScopes
|
|
| AppScopes
|
|
| AutomateScopes
|
|
| AutomateFunctionScopes
|
|
| WorkspaceScopes
|
|
|
|
/**
|
|
* All scopes
|
|
*/
|
|
export const AllScopes = flatMap(Scopes, (v) => Object.values(v))
|
|
|
|
export type ServerScope = (typeof AllScopes)[number]
|
|
|
|
export const isScope = (scope: unknown): scope is ServerScope =>
|
|
!!(scope && (AllScopes as unknown[]).includes(scope))
|
|
|
|
export const SourceAppNames = [
|
|
'Advance Steel',
|
|
'Dynamo',
|
|
'Revit',
|
|
'AutoCAD',
|
|
'Civil3D',
|
|
'Blender',
|
|
'Rhino',
|
|
'Grasshopper',
|
|
'GSA',
|
|
'Excel',
|
|
'Unity',
|
|
'Unreal',
|
|
'Python',
|
|
'.NET',
|
|
'IFC',
|
|
'QGIS',
|
|
'ArcGIS',
|
|
'ETABS',
|
|
'PowerBI',
|
|
'SketchUp',
|
|
'SAP2000',
|
|
'CSiBridge',
|
|
'SAFE',
|
|
'Archicad',
|
|
'Tekla Structures',
|
|
'OpenRoads',
|
|
'OpenRail',
|
|
'OpenBuildings',
|
|
'MicroStation',
|
|
'Navisworks',
|
|
'Speckle Automate',
|
|
'TopSolid',
|
|
'File Import',
|
|
'Acc integration'
|
|
] as const
|
|
|
|
export type SourceAppName = (typeof SourceAppNames)[number]
|
|
|
|
export type SourceAppDefinition = {
|
|
/**
|
|
* String to look for in input app names to match them to a specific source app
|
|
* this is an eqiuvalent of the connector slug
|
|
*/
|
|
searchKey: string
|
|
|
|
/**
|
|
* Full name
|
|
*/
|
|
name: SourceAppName
|
|
|
|
/**
|
|
* Shortened name
|
|
*/
|
|
short: string
|
|
|
|
/**
|
|
* BG color hex code for badges
|
|
*/
|
|
bgColor: string
|
|
}
|
|
|
|
export const SourceApps: SourceAppDefinition[] = [
|
|
// ordering of the list is important.
|
|
// this will match all acc file importers
|
|
{ searchKey: '-acc', name: 'Acc integration', short: 'ACC', bgColor: '#3091e7' },
|
|
{
|
|
// this will match all rhino based file importers
|
|
searchKey: '-rhino-importer',
|
|
name: 'File Import',
|
|
short: '',
|
|
bgColor: '#3091e7'
|
|
},
|
|
{ searchKey: 'advancesteel', name: 'Advance Steel', short: 'AS', bgColor: '#a438b6' },
|
|
// deliberately keeping dynamo in front of revit, so it preceeds it when resolving from the host app name
|
|
{ searchKey: 'dynamo', name: 'Dynamo', short: 'DYN', bgColor: '#a438b6' },
|
|
{ searchKey: 'revit', name: 'Revit', short: 'RVT', bgColor: '#3091e7' },
|
|
{ searchKey: 'autocad', name: 'AutoCAD', short: 'ACAD', bgColor: '#f0605e' },
|
|
{ searchKey: 'civil3d', name: 'Civil3D', short: 'C3D', bgColor: '#14c1d7' },
|
|
{ searchKey: 'blender', name: 'Blender', short: 'BLEND', bgColor: '#fb9514' },
|
|
{ searchKey: 'rhino', name: 'Rhino', short: 'RH', bgColor: '#141414' },
|
|
{ searchKey: 'grasshopper', name: 'Grasshopper', short: 'GH', bgColor: '#48974b' },
|
|
{ searchKey: 'excel', name: 'Excel', short: 'XLSX', bgColor: '#72c076' },
|
|
{ searchKey: 'unity', name: 'Unity', short: 'UNITY', bgColor: '#149e91' },
|
|
{ searchKey: 'unreal', name: 'Unreal', short: 'UE', bgColor: '#846256' },
|
|
{ searchKey: 'python', name: 'Python', short: 'PY', bgColor: '#fddb45' },
|
|
{ searchKey: 'net', name: '.NET', short: '.NET', bgColor: '#8531a9' },
|
|
{ searchKey: 'ifc', name: 'IFC', short: 'IFC', bgColor: '#bd2e2e' },
|
|
{ searchKey: 'qgis', name: 'QGIS', short: 'QGIS', bgColor: '#70e029' },
|
|
{ searchKey: 'gsa', name: 'GSA', short: 'GSA', bgColor: '#70e029' },
|
|
{ searchKey: 'arcgis', name: 'ArcGIS', short: 'AGIS', bgColor: '#3a6eff' },
|
|
{ searchKey: 'etabs', name: 'ETABS', short: 'EDB', bgColor: '#6d6d6d' },
|
|
{ searchKey: 'powerbi', name: 'PowerBI', short: 'PBI', bgColor: '#ffff96' },
|
|
{ searchKey: 'sketchup', name: 'SketchUp', short: 'SKP', bgColor: '#8cb7ff' },
|
|
{ searchKey: 'sap2000', name: 'SAP2000', short: 'SAP', bgColor: '#6d6d6d' },
|
|
{ searchKey: 'csibridge', name: 'CSiBridge', short: 'CSIB', bgColor: '#6d6d6d' },
|
|
{ searchKey: 'safe', name: 'SAFE', short: 'SAFE', bgColor: '#6d6d6d' },
|
|
{ searchKey: 'archicad', name: 'Archicad', short: 'ARCHI', bgColor: '#3091e7' },
|
|
{ searchKey: 'topsolid', name: 'TopSolid', short: 'TOPS', bgColor: '#3091e7' },
|
|
{
|
|
searchKey: 'teklastructures',
|
|
name: 'Tekla Structures',
|
|
short: 'TKL',
|
|
bgColor: '#3a6eff'
|
|
},
|
|
{ searchKey: 'openroads', name: 'OpenRoads', short: 'OROAD', bgColor: '#846256' },
|
|
{ searchKey: 'openrail', name: 'OpenRail', short: 'ORAIL', bgColor: '#846256' },
|
|
{
|
|
searchKey: 'openbuildings',
|
|
name: 'OpenBuildings',
|
|
short: 'OBUILD',
|
|
bgColor: '#846256'
|
|
},
|
|
{
|
|
searchKey: 'microstation',
|
|
name: 'MicroStation',
|
|
short: 'MICRO',
|
|
bgColor: '#846256'
|
|
},
|
|
{ searchKey: 'navisworks', name: 'Navisworks', bgColor: '#3e8742', short: 'NAVIS' },
|
|
{
|
|
searchKey: 'automate',
|
|
name: 'Speckle Automate',
|
|
bgColor: '#f85c56',
|
|
short: 'ATMAT'
|
|
}
|
|
]
|
|
|
|
export const WebhookTriggers = Object.freeze(<const>{
|
|
StreamUpdate: 'stream_update',
|
|
StreamDelete: 'stream_delete',
|
|
BranchCreate: 'branch_create',
|
|
BranchUpdate: 'branch_update',
|
|
BranchDelete: 'branch_delete',
|
|
CommitCreate: 'commit_create',
|
|
CommitUpdate: 'commit_update',
|
|
CommitReceive: 'commit_receive',
|
|
CommitDelete: 'commit_delete',
|
|
CommentCreated: 'comment_created',
|
|
CommentArchived: 'comment_archived',
|
|
CommentReplied: 'comment_replied',
|
|
StreamPermissionsAdd: 'stream_permissions_add',
|
|
StreamPermissionsRemove: 'stream_permissions_remove'
|
|
})
|
|
|
|
export const blockedDomains: string[] = [
|
|
// Common Free Email Providers
|
|
'gmail.com',
|
|
'yahoo.com',
|
|
'hotmail.com',
|
|
'outlook.com',
|
|
'live.com',
|
|
'aol.com',
|
|
'ymail.com',
|
|
'mail.com',
|
|
'protonmail.com',
|
|
'icloud.com',
|
|
'zoho.com',
|
|
'gmx.com',
|
|
'me.com',
|
|
'inbox.com',
|
|
|
|
// Temporary/Disposable Email Providers
|
|
'mailinator.com',
|
|
'10minutemail.com',
|
|
'guerrillamail.com',
|
|
'tempmail.com',
|
|
'yopmail.com',
|
|
'throwawaymail.com',
|
|
'temp-mail.org',
|
|
'maildrop.cc',
|
|
'getairmail.com',
|
|
'mintemail.com',
|
|
'fakemail.net',
|
|
'temp-mail.ru',
|
|
'moakt.com',
|
|
'emailondeck.com',
|
|
'spamgourmet.com',
|
|
'mailcatch.com',
|
|
'sharklasers.com',
|
|
'trashmail.com',
|
|
'mytrashmail.com',
|
|
'emailfake.com',
|
|
'fakeinbox.com',
|
|
'spamex.com',
|
|
'spambox.us',
|
|
'mailsac.com',
|
|
'fakemailgenerator.com',
|
|
'33mail.com',
|
|
'anonmails.de',
|
|
'anonbox.net',
|
|
'anonymousspeech.com',
|
|
'boun.cr',
|
|
'guerrillamailblock.com',
|
|
'mailfreeonline.com',
|
|
'temp-email.com',
|
|
'mailnesia.com',
|
|
'hmamail.com',
|
|
'fastmail.com',
|
|
'tmailinator.com',
|
|
'spam4.me',
|
|
'fakebox.com',
|
|
'emkei.cz',
|
|
'dispostable.com',
|
|
'mytemp.email',
|
|
'deadaddress.com',
|
|
'spamdecoy.net',
|
|
'0wnd.net',
|
|
'0wnd.org',
|
|
'10mail.org',
|
|
'20mail.it',
|
|
'20mail.in',
|
|
'24hourmail.com',
|
|
'2prong.com',
|
|
'3d-painting.com',
|
|
'4warding.com',
|
|
'4warding.net',
|
|
'4warding.org',
|
|
'5mail.cf',
|
|
'60minutemail.com',
|
|
'675hosting.com',
|
|
'675hosting.net',
|
|
'675hosting.org',
|
|
'6ip.us',
|
|
'6url.com',
|
|
'75hosting.com',
|
|
'75hosting.net',
|
|
'75hosting.org',
|
|
'7tags.com',
|
|
'9ox.net',
|
|
'a-bc.net',
|
|
'afrobacon.com',
|
|
'ajaxapp.net',
|
|
'amilegit.com',
|
|
'anonbox.net',
|
|
'antichef.com',
|
|
'antichef.net',
|
|
'antireg.ru',
|
|
'antispam.de',
|
|
'baxomale.ht.cx',
|
|
'beefmilk.com',
|
|
'binkmail.com',
|
|
'bio-muesli.net',
|
|
'bobmail.info',
|
|
'bofthew.com',
|
|
'brefmail.com',
|
|
'bsnow.net',
|
|
'bugmenot.com',
|
|
'bumpymail.com',
|
|
'casualdx.com',
|
|
'chogmail.com',
|
|
'cool.fr.nf',
|
|
'correo.blogos.net',
|
|
'cosmorph.com',
|
|
'courriel.fr.nf',
|
|
'cubiclink.com',
|
|
'curryworld.de',
|
|
'dacoolest.com',
|
|
'dandikmail.com',
|
|
'deadspam.com',
|
|
'despam.it',
|
|
'devnullmail.com',
|
|
'dfgh.net',
|
|
'digitalsanctuary.com',
|
|
'discardmail.com',
|
|
'dispose.it',
|
|
'disposableaddress.com',
|
|
'disposeamail.com',
|
|
'dispostable.com',
|
|
'dodgeit.com',
|
|
'dodgit.com',
|
|
'dodgit.org',
|
|
'dontreg.com',
|
|
'dontsendmespam.de'
|
|
]
|
|
|
|
export const blockedSlugs: string[] = ['actions']
|