Files
speckle-server/packages/server/modules/core/helpers/token.ts
T
Kristaps Fabians Geikins 0b2ca9a515 feat: add missing FE1 fields to the FE2 GQL schema (#2471)
* WIP version create

* commitCreate migrated

* minor cleanup

* commitReceived migrated

* added Project.object

* Project.comment introduced

* moving away old API usages in FE1

* ProjectMutations.batchDelete

* project pending access requests

* WIP project access req tests

* project access req tests done

* ModelByName test

* version mutation tests

* project.object tests

* batch delete tests

* minor improvements to redirect logging
2024-07-10 11:33:53 +02:00

145 lines
4.1 KiB
TypeScript

import {
TokenResourceIdentifier,
TokenResourceIdentifierType
} from '@/modules/core/domain/tokens/types'
import { TokenCreateError } from '@/modules/core/errors/user'
import { TokenResourceAccessRecord } from '@/modules/core/helpers/types'
import { ResourceTargets } from '@/modules/serverinvites/helpers/inviteHelper'
import { MaybeNullOrUndefined, Nullable, Optional, Scopes } from '@speckle/shared'
import { differenceBy } from 'lodash'
export type ContextResourceAccessRules = MaybeNullOrUndefined<TokenResourceIdentifier[]>
export const resourceAccessRuleToIdentifier = (
rule: TokenResourceAccessRecord
): TokenResourceIdentifier => {
return {
id: rule.resourceId,
type: rule.resourceType
}
}
export const roleResourceTypeToTokenResourceType = (
type: string
): Nullable<TokenResourceIdentifierType> => {
switch (type) {
case ResourceTargets.Streams:
return 'project'
default:
return null
}
}
export const isResourceAllowed = (params: {
resourceId: string
resourceType: TokenResourceIdentifierType
resourceAccessRules?: MaybeNullOrUndefined<TokenResourceIdentifier[]>
}) => {
const { resourceId, resourceType, resourceAccessRules } = params
const relevantRules = resourceAccessRules?.filter((r) => r.type === resourceType)
return !relevantRules?.length || relevantRules.some((r) => r.id === resourceId)
}
export const isNewResourceAllowed = (params: {
resourceType: TokenResourceIdentifierType
resourceAccessRules?: MaybeNullOrUndefined<TokenResourceIdentifier[]>
}) => {
const { resourceType, resourceAccessRules } = params
const relevantRules = resourceAccessRules?.filter((r) => r.type === resourceType)
return !relevantRules?.length
}
export const toProjectIdWhitelist = (
resourceAccessRules: ContextResourceAccessRules
): Optional<string[]> => {
const projectRules = resourceAccessRules?.filter((r) => r.type === 'project')
return projectRules?.map((r) => r.id)
}
const canCreateToken = (params: {
scopes: {
user: string[]
token: string[]
}
limitedResources?: {
user: MaybeNullOrUndefined<TokenResourceIdentifier[]>
token: MaybeNullOrUndefined<TokenResourceIdentifier[]>
}
}) => {
const { scopes, limitedResources } = params
const hasAllScopes = scopes.token.every((scope) => scopes.user.includes(scope))
if (!hasAllScopes) {
throw new TokenCreateError(
"You can't create a token with scopes that you don't have"
)
}
const userLimitedResources = limitedResources?.user
const tokenLimitedResources = limitedResources?.token
let throwAboutInvalidResources = false
if (userLimitedResources?.length || tokenLimitedResources?.length) {
if (userLimitedResources?.length && !tokenLimitedResources?.length) {
throwAboutInvalidResources = true
} else if (userLimitedResources?.length) {
const disallowedResources = differenceBy(
tokenLimitedResources || [],
userLimitedResources || [],
(r) => `${r.type}:${r.id}`
)
if (disallowedResources.length) {
throwAboutInvalidResources = true
}
}
}
if (throwAboutInvalidResources) {
throw new TokenCreateError(
`You can't create a token with access to resources that you don't currently have access to`
)
}
return true
}
export const canCreatePAT = (params: {
scopes: {
user: string[]
token: string[]
}
}) => {
const { scopes } = params
if (scopes.token.includes(Scopes.Tokens.Write)) {
throw new TokenCreateError(
"You can't create a personal access token with the tokens:write scope"
)
}
return canCreateToken(params)
}
export const canCreateAppToken = (params: {
scopes: {
user: string[]
token: string[]
}
appId: {
user: string
token: string
}
limitedResources: {
user: MaybeNullOrUndefined<TokenResourceIdentifier[]>
token: MaybeNullOrUndefined<TokenResourceIdentifier[]>
}
}) => {
const { appId } = params
if (appId.user !== appId.token || !appId.token?.length || !appId.user?.length) {
throw new TokenCreateError(
'An app token can only create a new token for the same app'
)
}
return canCreateToken(params)
}