Files
speckle-server/packages/server/modules/automate/services/authCode.ts
T
Chuck Driesler e312110933 Automate Public Beta (#3472)
* feat(automate): query active user functions

* fix(automate): show automations to non-stream-owners

* feat(automate): associate function with workspace

* fix(automate): split functions page between user and example functions

* fix(automate): ugh

* fix(functions): use correct query type in different places

* fix(automate): workspace functions page

* feat(automate): query specific categories of functions

* fix(automate): checkpoint

* fix(workspaces): successful queries w local env

* fix(automate): createFunctionWithoutVersion

* fix(automate): successful associate function with workspace

* fix(automate): query and return workspaces on functions

* fix(automate): show current function workspace

* fix(automate): query functions in automation create dialog

* fix(automate): audit non-owner automation access

* refactor(automate): logs api can get the projectId from the path

* fix(automate): multiregion gql resolvers

* fix(automate): multiregion event listeners

* fix(automate): drop automationCount

* fix(automate): multiregion run status

* fix(automate): correctness

* fix(automate): successful usage of multiregion results

* fix(automate): actually finish event listeners

* chore(automate): fix tests fix tests

* fix(automate): fix tests but make it multiregion flavor

* fix(automate): logs endpoint

* fix(automate): inject projectid correctly

* fix(automate): drop user-source functions

* fix(automate): owners edit, others can view

* fix(automate): simplify queries, auto workspace association

* chore(automate): appease

* chore(automate): fix function types

* fix(automate): get to workspace functions from empty state

* chore(automate): death to all slugs

* fix(automate): no create automation from function

* fix(automate): hide workspace change, tweak role access

---------

Co-authored-by: Gergő Jedlicska <gergo@jedlicska.com>
2024-11-29 17:33:14 +01:00

85 lines
2.6 KiB
TypeScript

import { automateLogger } from '@/logging/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'
export enum AuthCodePayloadAction {
CreateAutomation = 'createAutomation',
CreateFunction = 'createFunction',
ListWorkspaceFunctions = 'listWorkspaceFunctions',
ListUserFunctions = 'listUserFunctions',
BecomeFunctionAuthor = 'becomeFunctionAuthor',
GetAvailableGithubOrganizations = 'getAvailableGithubOrganizations',
UpdateFunction = 'updateFunction'
}
export type AuthCodePayload = {
code: string
userId: string
workspaceId?: 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; emit: EventBus['emit'] }) =>
async (payload: AuthCodePayload) => {
const { redis, emit } = deps
const potentialPayloadString = await redis.get(payload.code)
const potentialPayload: unknown = potentialPayloadString
? JSON.parse(potentialPayloadString)
: null
const formattedPayload = isPayload(potentialPayload) ? potentialPayload : null
if (
!formattedPayload ||
formattedPayload.code !== payload.code ||
formattedPayload.userId !== payload.userId ||
formattedPayload.action !== payload.action
) {
throw new AutomateAuthCodeHandshakeError('Invalid automate auth payload')
}
if (payload.workspaceId) {
emit({
eventName: 'workspace.authorized',
payload: { userId: payload.userId, workspaceId: payload.workspaceId }
})
}
try {
await redis.del(payload.code)
} catch (e) {
automateLogger.error(e, 'Auth code deletion unexpectedly failed')
}
return true
}