Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { User } from '../../auth/dtos'
import { Functions } from '../../features/functions/interface'
import { Rules } from '../../features/rules/interface'
import { services } from '../../services'
import { generateContextData } from '../context/helpers'
import { contextUserForRun, generateContextData, REALM_SYSTEM_USER } from '../context/helpers'

const originalEnv = process.env

Expand Down Expand Up @@ -113,4 +113,49 @@ describe('generateContextData', () => {
const binaryObject = Binary.fromBase64Binary(base64, 0)
expect(binaryObject).toBeInstanceOf(BSON.Binary)
})

it('exposes Realm system user and runningAsSystem when run_as_system is enabled', () => {
const mockApp = Fastify()
const systemFunction = { ...currentFunction, run_as_system: true }
const { context } = generateContextData({
services,
app: mockApp,
functionsList: mockFunctions,
currentFunction: systemFunction,
GenerateContext: GenerateContextMock,
GenerateContextSync: GenerateContextSyncMock,
user: REALM_SYSTEM_USER,
rules: mockRules
})

expect(context.user).toEqual(REALM_SYSTEM_USER)
expect(context.runningAsSystem()).toBe(true)
})

it('runningAsSystem is false for regular function execution', () => {
const mockApp = Fastify()
const { context } = generateContextData({
services,
app: mockApp,
functionsList: mockFunctions,
currentFunction,
GenerateContext: GenerateContextMock,
GenerateContextSync: GenerateContextSyncMock,
user: { id: 'user-1', type: 'normal', data: {}, custom_data: {}, identities: [] },
rules: mockRules
})

expect(context.runningAsSystem()).toBe(false)
})
})

describe('contextUserForRun', () => {
it('returns Realm system user for empty user with run_as_system', () => {
expect(contextUserForRun({}, true)).toEqual(REALM_SYSTEM_USER)
})

it('returns the user unchanged when not running as system', () => {
const user = { id: 'user-1' }
expect(contextUserForRun(user, false)).toBe(user)
})
})
17 changes: 16 additions & 1 deletion packages/flowerbase/src/utils/context/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { mongodb } from '@fastify/mongodb'
import { EJSON } from 'bson'
import * as jwt from 'jsonwebtoken'
import { Arguments } from '../../auth/dtos'
import { Arguments, User } from '../../auth/dtos'
import { Function } from '../../features/functions/interface'
import { GenerateContextDataParams } from './interface'

/** Realm-compatible system user for run_as_system execution with no authenticated caller. */
export const REALM_SYSTEM_USER = {
type: 'system',
data: {},
custom_data: {},
identities: []
} as const

const isEmptyUser = (user: User) =>
user != null && typeof user === 'object' && !Array.isArray(user) && Object.keys(user).length === 0

export const contextUserForRun = (user: User, runAsSystem: boolean): User =>
runAsSystem && isEmptyUser(user) ? REALM_SYSTEM_USER : user

type JwtUtils = {
encode: (
signingMethod: string,
Expand Down Expand Up @@ -207,6 +221,7 @@ export const generateContextData = ({
action: ''
},
user,
runningAsSystem: () => Boolean(currentFunction.run_as_system),
environment: {
tag: process.env.NODE_ENV
},
Expand Down
8 changes: 5 additions & 3 deletions packages/flowerbase/src/utils/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import vm from 'vm'
import { EJSON } from 'bson'
import { StateManager } from '../../state'
import { Function as AppFunction } from '../../features/functions/interface'
import { generateContextData } from './helpers'
import { contextUserForRun, generateContextData } from './helpers'
import { GenerateContextParams } from './interface'

const dynamicImport = new Function('specifier', 'return import(specifier)') as (
Expand Down Expand Up @@ -323,11 +323,12 @@ export async function GenerateContext({
const functionsQueue = StateManager.select("functionsQueue")
const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
const contextUser = contextUserForRun(user, effectiveRunAsSystem)

const run = async () => {

const contextData = generateContextData({
user,
user: contextUser,
services,
app,
rules,
Expand Down Expand Up @@ -443,8 +444,9 @@ export function GenerateContextSync({

const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
const contextUser = contextUserForRun(user, effectiveRunAsSystem)
const contextData = generateContextData({
user,
user: contextUser,
services,
app,
rules,
Expand Down
Loading