Skip to content

Commit b6ac3d0

Browse files
committed
Auto-discover and ask to update the defaultOrg and enforcedOrgs config keys
1 parent 6475f9b commit b6ac3d0

File tree

9 files changed

+264
-24
lines changed

9 files changed

+264
-24
lines changed

src/commands/config/cmd-config-get.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ describe('socket config get', async () => {
2929
Keys:
3030
3131
- apiBaseUrl -- Base URL of the API endpoint
32-
- apiToken -- The API token required to access most API endpoints
3332
- apiProxy -- A proxy through which to access the API
33+
- apiToken -- The API token required to access most API endpoints
3434
- defaultOrg -- The default org slug to use when appropriate; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.
3535
- enforcedOrgs -- Orgs in this list have their security policies enforced on this machine
3636

src/commands/config/cmd-config-list.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ describe('socket config get', async () => {
3030
Keys:
3131
3232
- apiBaseUrl -- Base URL of the API endpoint
33-
- apiToken -- The API token required to access most API endpoints
3433
- apiProxy -- A proxy through which to access the API
34+
- apiToken -- The API token required to access most API endpoints
3535
- defaultOrg -- The default org slug to use when appropriate; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.
3636
- enforcedOrgs -- Orgs in this list have their security policies enforced on this machine
3737

src/commands/config/cmd-config-set.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe('socket config get', async () => {
3434
Keys:
3535
3636
- apiBaseUrl -- Base URL of the API endpoint
37-
- apiToken -- The API token required to access most API endpoints
3837
- apiProxy -- A proxy through which to access the API
38+
- apiToken -- The API token required to access most API endpoints
3939
- defaultOrg -- The default org slug to use when appropriate; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.
4040
- enforcedOrgs -- Orgs in this list have their security policies enforced on this machine
4141

src/commands/config/cmd-config-unset.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ describe('socket config unset', async () => {
2929
Keys:
3030
3131
- apiBaseUrl -- Base URL of the API endpoint
32-
- apiToken -- The API token required to access most API endpoints
3332
- apiProxy -- A proxy through which to access the API
33+
- apiToken -- The API token required to access most API endpoints
3434
- defaultOrg -- The default org slug to use when appropriate; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.
3535
- enforcedOrgs -- Orgs in this list have their security policies enforced on this machine
3636
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { handleApiCall } from '../../utils/api'
2+
import { supportedConfigKeys } from '../../utils/config'
3+
import { getDefaultToken, setupSdk } from '../../utils/sdk'
4+
5+
import type { LocalConfig } from '../../utils/config'
6+
7+
export async function discoverConfigValue(
8+
key: string
9+
): Promise<{ success: boolean; value: unknown; message: string }> {
10+
// This will have to be a specific implementation per key because certain
11+
// keys should request information from particular API endpoints while
12+
// others should simply return their default value, like endpoint URL.
13+
14+
if (!supportedConfigKeys.has(key as keyof LocalConfig)) {
15+
return {
16+
success: false,
17+
value: undefined,
18+
message: 'Requested key is not a valid config key.'
19+
}
20+
}
21+
22+
if (key === 'apiBaseUrl') {
23+
// Return the default value
24+
return {
25+
success: false,
26+
value: undefined,
27+
message:
28+
"If you're unsure about the base endpoint URL then simply unset it."
29+
}
30+
}
31+
32+
if (key === 'apiProxy') {
33+
// I don't think we can auto-discover this with any order of reliability..?
34+
return {
35+
success: false,
36+
value: undefined,
37+
message:
38+
'When uncertain, unset this key. Otherwise ask your network administrator.'
39+
}
40+
}
41+
42+
if (key === 'apiToken') {
43+
return {
44+
success: false,
45+
value: undefined,
46+
message:
47+
'You can find/create your API token in your Socket dashboard > settings > API tokens.\nYou should then use `socket login` to login instead of this command.'
48+
}
49+
}
50+
51+
if (key === 'defaultOrg') {
52+
const apiToken = getDefaultToken()
53+
if (!apiToken) {
54+
return {
55+
success: false,
56+
value: undefined,
57+
message:
58+
'No API token set, must have a token to resolve its default org.'
59+
}
60+
}
61+
62+
const org = await getDefaultOrgFromToken()
63+
if (!org?.length) {
64+
return {
65+
success: false,
66+
value: undefined,
67+
message:
68+
'Was unable to determine default org for the current API token.'
69+
}
70+
}
71+
72+
if (Array.isArray(org)) {
73+
return {
74+
success: true,
75+
value: org,
76+
message: 'These are the orgs that the current API token can access.'
77+
}
78+
}
79+
80+
return {
81+
success: true,
82+
value: org,
83+
message: 'This is the org that belongs to the current API token.'
84+
}
85+
}
86+
87+
if (key === 'enforcedOrgs') {
88+
const apiToken = getDefaultToken()
89+
if (!apiToken) {
90+
return {
91+
success: false,
92+
value: undefined,
93+
message:
94+
'No API token set, must have a token to resolve orgs to enforce.'
95+
}
96+
}
97+
98+
const orgs = await getEnforceableOrgsFromToken()
99+
if (!orgs?.length) {
100+
return {
101+
success: false,
102+
value: undefined,
103+
message:
104+
'Was unable to determine any orgs to enforce for the current API token.'
105+
}
106+
}
107+
108+
return {
109+
success: true,
110+
value: orgs,
111+
message: 'These are the orgs whose security policy you can enforce.'
112+
}
113+
}
114+
115+
if (key === 'test') {
116+
return {
117+
success: false,
118+
value: undefined,
119+
message: ''
120+
}
121+
}
122+
123+
// Mostly to please TS, because we're not telling it `key` is keyof LocalConfig
124+
return {
125+
success: false,
126+
value: undefined,
127+
message: 'unreachable?'
128+
}
129+
}
130+
131+
async function getDefaultOrgFromToken(): Promise<
132+
string[] | string | undefined
133+
> {
134+
const sockSdk = await setupSdk()
135+
const result = await handleApiCall(
136+
sockSdk.getOrganizations(),
137+
'looking up organizations'
138+
)
139+
140+
if (result.success) {
141+
const arr = Array.from(Object.values(result.data.organizations)).map(
142+
({ slug }) => slug
143+
)
144+
if (arr.length === 0) {
145+
return undefined
146+
}
147+
if (arr.length === 1) {
148+
return arr[0]
149+
}
150+
return arr
151+
}
152+
153+
return undefined
154+
}
155+
156+
async function getEnforceableOrgsFromToken(): Promise<string[] | undefined> {
157+
const sockSdk = await setupSdk()
158+
const result = await handleApiCall(
159+
sockSdk.getOrganizations(),
160+
'looking up organizations'
161+
)
162+
163+
if (result.success) {
164+
const arr = Array.from(Object.values(result.data.organizations)).map(
165+
({ slug }) => slug
166+
)
167+
if (arr.length === 0) {
168+
return undefined
169+
}
170+
return arr
171+
}
172+
173+
return undefined
174+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { outputConfigGet } from './output-config-get'
2-
import { getConfigValue } from '../../utils/config'
1+
import { discoverConfigValue } from './discover-config-value'
2+
import { outputConfigAuto } from './output-config-auto'
33

44
import type { LocalConfig } from '../../utils/config'
55

@@ -10,7 +10,7 @@ export async function handleConfigAuto({
1010
key: keyof LocalConfig
1111
outputKind: 'json' | 'markdown' | 'text'
1212
}) {
13-
const value = getConfigValue(key)
13+
const result = await discoverConfigValue(key)
1414

15-
await outputConfigGet(key, value, outputKind)
15+
await outputConfigAuto(key, result, outputKind)
1616
}

src/commands/config/output-config-auto.ts

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { logger } from '@socketsecurity/registry/lib/logger'
2+
import { select } from '@socketsecurity/registry/lib/prompts'
23

3-
import { LocalConfig } from '../../utils/config'
4+
import { LocalConfig, updateConfigValue } from '../../utils/config'
45

56
export async function outputConfigAuto(
67
key: keyof LocalConfig,
7-
success: boolean,
8-
value: unknown,
9-
message: string,
8+
{
9+
message,
10+
success,
11+
value
12+
}: {
13+
success: boolean
14+
value: unknown
15+
message: string
16+
},
1017
outputKind: 'json' | 'markdown' | 'text'
1118
) {
1219
if (outputKind === 'json') {
@@ -20,11 +27,70 @@ export async function outputConfigAuto(
2027
logger.log('')
2128
if (success) {
2229
logger.log(`The discovered value is: "${value}"`)
30+
if (message) {
31+
logger.log('')
32+
logger.log(message)
33+
}
2334
} else {
2435
logger.log(`The discovery failed: ${message}`)
2536
}
2637
logger.log('')
2738
} else {
28-
logger.log(`${key}: ${value}`)
39+
if (message) {
40+
logger.log(message)
41+
logger.log('')
42+
}
43+
logger.log(`- ${key}: ${value}`)
44+
logger.log('')
45+
46+
if (success) {
47+
if (key === 'defaultOrg') {
48+
const proceed = await select<string>({
49+
message:
50+
'Would you like to update the default org in local config to this value?',
51+
choices: (Array.isArray(value) ? value : [value])
52+
.map(slug => ({
53+
name: 'Yes [' + slug + ']',
54+
value: slug,
55+
description: `Use "${slug}" as the default organization`
56+
}))
57+
.concat({
58+
name: 'No',
59+
value: '',
60+
description: 'Do not use any of these organizations'
61+
})
62+
})
63+
if (proceed) {
64+
logger.log(
65+
`OK. Setting defaultOrg to "${proceed}".\nYou should no longer need to add the org to commands that normally require it.`
66+
)
67+
updateConfigValue('defaultOrg', proceed)
68+
} else {
69+
logger.log('OK. No changes made.')
70+
}
71+
} else if (key === 'enforcedOrgs') {
72+
const proceed = await select<string>({
73+
message:
74+
'Would you like to update the enforced orgs in local config to this value?',
75+
choices: (Array.isArray(value) ? value : [value])
76+
.map(slug => ({
77+
name: 'Yes [' + slug + ']',
78+
value: slug,
79+
description: `Enforce the security policy of "${slug}" on this machine`
80+
}))
81+
.concat({
82+
name: 'No',
83+
value: '',
84+
description: 'Do not use any of these organizations'
85+
})
86+
})
87+
if (proceed) {
88+
logger.log(`OK. Setting enforcedOrgs key to "${proceed}".`)
89+
updateConfigValue('defaultOrg', proceed)
90+
} else {
91+
logger.log('OK. No changes made.')
92+
}
93+
}
94+
}
2995
}
3096
}

src/commands/organization/cmd-organization-policy-security.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ describe('socket organization list', async () => {
6060
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
6161
expect(stdout).toMatchInlineSnapshot(`""`)
6262
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
63-
"
64-
_____ _ _ /---------------
65-
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
66-
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
67-
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy security\`, cwd: <redacted>
63+
"
64+
_____ _ _ /---------------
65+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
66+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
67+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy security\`, cwd: <redacted>
6868
69-
\\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields:
69+
\\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields:
7070
71-
- Org name as the first argument \\x1b[31m(missing!)\\x1b[39m
72-
- The json and markdown flags cannot be both set \\x1b[32m(ok)\\x1b[39m"
73-
`)
71+
- Org name as the first argument \\x1b[31m(missing!)\\x1b[39m
72+
- The json and markdown flags cannot be both set \\x1b[32m(ok)\\x1b[39m"
73+
`)
7474

7575
expect(code, 'dry-run should exit with code 2 if input bad').toBe(2)
7676
}

src/utils/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ export interface LocalConfig {
2222
test?: unknown
2323
}
2424

25-
// Dugefault app data folder env var on Win
25+
// Default app data folder env var on Win
2626
const LOCALAPPDATA = 'LOCALAPPDATA'
2727
// Default app data folder env var on Mac/Linux
2828
const XDG_DATA_HOME = 'XDG_DATA_HOME'
2929
const SOCKET_APP_DIR = 'socket/settings' // It used to be settings...
3030

3131
export const supportedConfigKeys: Map<keyof LocalConfig, string> = new Map([
3232
['apiBaseUrl', 'Base URL of the API endpoint'],
33-
['apiToken', 'The API token required to access most API endpoints'],
3433
['apiProxy', 'A proxy through which to access the API'],
34+
['apiToken', 'The API token required to access most API endpoints'],
3535
[
3636
'defaultOrg',
3737
'The default org slug to use when appropriate; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.'

0 commit comments

Comments
 (0)