Files
speckle-server/packages/server/modules/automate/services/authCode.ts
T
Kristaps Fabians Geikins bde148f286 chore(server): migrating fully to ESM (#5042)
* wip

* some extra fixes

* stuff kinda works?

* need to figure out mocks

* need to figure out mocks

* fix db listener

* gqlgen fix

* minor gqlgen watch adjustment

* lint fixes

* delete old codegen file

* converting migrations to ESM

* getModuleDIrectory

* vitest sort of works

* added back ts-vitest

* resolve gql double load

* fixing test timeout configs

* TSC lint fix

* fix automate tests

* moar debugging

* debugging

* more debugging

* codegen update

* server works

* yargs migrated

* chore(server): getting rid of global mocks for Server ESM (#5046)

* got rid of email mock

* got rid of comment mocks

* got rid of multi region mocks

* got rid of stripe mock

* admin override mock updated

* removed final mock

* fixing import.meta.resolve calls

* another import.meta.resolve fix

* added requested test

* nyc ESM fix

* removed unneeded deps + linting

* yarn lock forgot to commit

* tryna fix flakyness

* email capture util fix

* sendEmail fix

* fix TSX check

* sender transporter fix + CR comments

* merge main fix

* test fixx

* circleci fix

* gqlgen bigint fix

* error formatter fix

* more error formatting improvements

* esmloader added to Dockerfile

* more dockerfile fixes

* bg jobs fix
2025-07-14 10:26:19 +03:00

104 lines
3.1 KiB
TypeScript

import { automateLogger } from '@/observability/logging'
import { CreateStoredAuthCode } from '@/modules/automate/domain/operations'
import { AutomateAuthCodeHandshakeError } from '@/modules/automate/errors/management'
import { EventBus } from '@/modules/shared/services/eventBus'
import cryptoRandomString from 'crypto-random-string'
import Redis from 'ioredis'
import { get, has, isObjectLike } from 'lodash-es'
import { Logger } from 'pino'
import { WorkspaceEvents } from '@/modules/workspacesCore/domain/events'
export enum AuthCodePayloadAction {
CreateAutomation = 'createAutomation',
CreateFunction = 'createFunction',
ListWorkspaceFunctions = 'listWorkspaceFunctions',
ListUserFunctions = 'listUserFunctions',
BecomeFunctionAuthor = 'becomeFunctionAuthor',
GetAvailableGithubOrganizations = 'getAvailableGithubOrganizations',
UpdateFunction = 'updateFunction'
}
export type AuthCodePayload = {
code: string
userId: string
action: AuthCodePayloadAction
}
const isPayload = (payload: unknown): payload is AuthCodePayload =>
!!(
payload &&
isObjectLike(payload) &&
has(payload, 'code') &&
has(payload, 'userId') &&
has(payload, 'action') &&
Object.values(AuthCodePayloadAction).includes(get(payload, 'action'))
)
export const createStoredAuthCodeFactory =
(deps: { redis: Redis }): CreateStoredAuthCode =>
async (params: Omit<AuthCodePayload, 'code'>) => {
const { redis } = deps
const payload: AuthCodePayload = {
...params,
code: cryptoRandomString({ length: 20 })
}
await redis.set(payload.code, JSON.stringify(payload), 'EX', 60 * 5)
return payload
}
export const validateStoredAuthCodeFactory =
(deps: { redis: Redis; logger: Logger; emit: EventBus['emit'] }) =>
async (params: {
payload: AuthCodePayload
resources?: {
workspaceId?: string
}
}) => {
const { redis, logger, emit } = deps
const { payload, resources } = params
const potentialPayloadString = await redis.get(payload.code)
const potentialPayload: unknown = potentialPayloadString
? JSON.parse(potentialPayloadString)
: null
const formattedPayload = isPayload(potentialPayload) ? potentialPayload : null
logger.info(
{
payloadString: potentialPayloadString,
payload: {
...formattedPayload,
code: null
}
},
'Validating execution engine request with provided auth payload.'
)
if (
!formattedPayload ||
formattedPayload.code !== payload.code ||
formattedPayload.userId !== payload.userId ||
formattedPayload.action !== payload.action
) {
throw new AutomateAuthCodeHandshakeError('Invalid automate auth payload')
}
// Token is valid, confirm user is authorized to access specified resources.
if (resources?.workspaceId) {
await emit({
eventName: WorkspaceEvents.Authorizing,
payload: { userId: payload.userId, workspaceId: resources?.workspaceId }
})
}
try {
await redis.del(payload.code)
} catch (e) {
automateLogger.error(e, 'Auth code deletion unexpectedly failed')
}
return true
}