Skip to content
This repository has been archived by the owner on Nov 4, 2021. It is now read-only.

Plugins access control #231

Merged
merged 5 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
18 changes: 9 additions & 9 deletions src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { clearError, processError } from './error'
import { getPluginAttachmentRows, getPluginConfigRows, getPluginRows } from './sql'
import { status } from './status'
import { PluginConfig, PluginJsonConfig, PluginsServer, PluginTask, TeamId } from './types'
import { getFileFromArchive } from './utils'
import { getFileFromArchive, pluginDigest } from './utils'
import { createPluginConfigVM } from './vm/vm'

export async function setupPlugins(server: PluginsServer): Promise<void> {
Expand Down Expand Up @@ -119,7 +119,7 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr
await processError(
server,
pluginConfig,
`Could not load posthog config at "${configPath}" for plugin "${plugin.name}"`
`Could not load posthog config at "${configPath}" for ${pluginDigest(plugin)}`
)
return false
}
Expand All @@ -129,7 +129,7 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr
await processError(
server,
pluginConfig,
`No "main" config key or "index.js" file found for plugin "${plugin.name}"`
`No "main" config key or "index.js" file found for ${pluginDigest(plugin)}`
)
return false
}
Expand All @@ -145,7 +145,7 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr

try {
pluginConfig.vm = await createPluginConfigVM(server, pluginConfig, indexJs, libJs)
status.info('🔌', `Loaded local plugin "${plugin.name}" from "${pluginPath}"!`)
status.info('🔌', `Loaded local ${pluginDigest(plugin)} from "${pluginPath}"!`)
await clearError(server, pluginConfig)
return true
} catch (error) {
Expand All @@ -159,7 +159,7 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr
try {
config = JSON.parse(json)
} catch (error) {
await processError(server, pluginConfig, `Can not load plugin.json for plugin "${plugin.name}"`)
await processError(server, pluginConfig, `Can not load plugin.json for ${pluginDigest(plugin)}`)
return false
}
}
Expand All @@ -173,19 +173,19 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr
if (indexJs) {
try {
pluginConfig.vm = await createPluginConfigVM(server, pluginConfig, indexJs, libJs || '')
status.info('🔌', `Loaded plugin "${plugin.name}"!`)
status.info('🔌', `Loaded ${pluginDigest(plugin)}!`)
await clearError(server, pluginConfig)
return true
} catch (error) {
await processError(server, pluginConfig, error)
}
} else {
await processError(server, pluginConfig, `Could not load index.js for plugin "${plugin.name}"!`)
await processError(server, pluginConfig, `Could not load index.js for ${pluginDigest(plugin)}!`)
}
} else if (plugin.plugin_type === 'source' && plugin.source) {
try {
pluginConfig.vm = await createPluginConfigVM(server, pluginConfig, plugin.source)
status.info('🔌', `Loaded plugin "${plugin.name}"!`)
status.info('🔌', `Loaded ${pluginDigest(plugin)}!`)
await clearError(server, pluginConfig)
return true
} catch (error) {
Expand All @@ -195,7 +195,7 @@ async function loadPlugin(server: PluginsServer, pluginConfig: PluginConfig): Pr
await processError(
server,
pluginConfig,
`Un-downloaded remote plugins not supported! Plugin: "${plugin.name}"`
`Un-downloaded remote plugins not supported! Tried with ${pluginDigest(plugin)}`
)
}
} catch (error) {
Expand Down
34 changes: 17 additions & 17 deletions src/sql.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import { Plugin, PluginAttachmentDB, PluginConfig, PluginConfigId, PluginError, PluginsServer } from './types'

function pluginConfigsInForceQuery(specificField?: string): string {
return `SELECT posthog_pluginconfig.${specificField || '*'}
FROM posthog_pluginconfig
LEFT JOIN posthog_team ON posthog_team.id = posthog_pluginconfig.team_id
LEFT JOIN posthog_organization ON posthog_organization.id = posthog_team.organization_id
LEFT JOIN posthog_plugin ON posthog_plugin.id = posthog_pluginconfig.plugin_id
WHERE (
posthog_pluginconfig.enabled='t' AND posthog_organization.plugins_access_level > 0
AND (posthog_plugin.organization_id = posthog_organization.id OR posthog_plugin.is_global)
)`
}

export async function getPluginRows(server: PluginsServer): Promise<Plugin[]> {
const { rows: pluginRows }: { rows: Plugin[] } = await server.db.postgresQuery(
`SELECT posthog_plugin.* FROM posthog_plugin WHERE id in
(SELECT posthog_pluginconfig.plugin_id
FROM posthog_pluginconfig
LEFT JOIN posthog_team ON posthog_team.id = posthog_pluginconfig.team_id
WHERE (posthog_team.id IS NULL OR posthog_team.plugins_opt_in='t') AND posthog_pluginconfig.enabled='t'
GROUP BY posthog_pluginconfig.plugin_id)`
`SELECT posthog_plugin.* FROM posthog_plugin
WHERE id IN (${pluginConfigsInForceQuery('plugin_id')} GROUP BY posthog_pluginconfig.plugin_id)`
)
return pluginRows
}

export async function getPluginAttachmentRows(server: PluginsServer): Promise<PluginAttachmentDB[]> {
const { rows }: { rows: PluginAttachmentDB[] } = await server.db.postgresQuery(
`SELECT posthog_pluginattachment.* FROM posthog_pluginattachment WHERE plugin_config_id in
(SELECT posthog_pluginconfig.id
FROM posthog_pluginconfig
LEFT JOIN posthog_team ON posthog_team.id = posthog_pluginconfig.team_id
WHERE (posthog_team.id IS NULL OR posthog_team.plugins_opt_in='t') AND posthog_pluginconfig.enabled='t')`
`SELECT posthog_pluginattachment.* FROM posthog_pluginattachment
WHERE plugin_config_id IN (${pluginConfigsInForceQuery('id')})`
)
return rows
}

export async function getPluginConfigRows(server: PluginsServer): Promise<PluginConfig[]> {
const { rows }: { rows: PluginConfig[] } = await server.db.postgresQuery(
`SELECT posthog_pluginconfig.*
FROM posthog_pluginconfig
LEFT JOIN posthog_team ON posthog_team.id = posthog_pluginconfig.team_id
WHERE (posthog_team.id IS NULL OR posthog_team.plugins_opt_in='t') AND posthog_pluginconfig.enabled='t'`
)
const { rows }: { rows: PluginConfig[] } = await server.db.postgresQuery(pluginConfigsInForceQuery())
return rows
}

Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface Plugin {
name: string
plugin_type: 'local' | 'respository' | 'custom' | 'source'
description?: string
is_global: boolean
url?: string
config_schema: Record<string, PluginConfigSchema> | PluginConfigSchema[]
tag?: string
Expand Down
10 changes: 9 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as tar from 'tar-stream'
import * as zlib from 'zlib'

import { status } from './status'
import { LogLevel, PluginsServerConfig, TimestampFormat } from './types'
import { LogLevel, Plugin, PluginsServerConfig, TimestampFormat } from './types'

/** Time until autoexit (due to error) gives up on graceful exit and kills the process right away. */
const GRACEFUL_EXIT_PERIOD_SECONDS = 5
Expand Down Expand Up @@ -386,3 +386,11 @@ export async function createRedis(serverConfig: PluginsServerConfig): Promise<Re
await redis.info()
return redis
}

export function pluginDigest(plugin: Plugin): string {
const extras = [`organization ${plugin.organization_id}`]
if (plugin.is_global) {
extras.push('GLOBAL')
}
return `plugin "${plugin.name}" (${extras.join(' - ')})`
}
1 change: 1 addition & 0 deletions tests/helpers/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const plugin60: Plugin = {
error: undefined,
from_json: false,
from_web: false,
is_global: false,
}

export const pluginAttachment1: PluginAttachmentDB = {
Expand Down