@microsoft/agents-hosting 1.9.0-beta.8.gf58e66b2ee → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -4
- package/dist/package.json +3 -3
- package/dist/src/app/agentApplication.d.ts +4 -3
- package/dist/src/app/agentApplication.js +24 -21
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +9 -2
- package/dist/src/app/agentApplicationBuilder.js +9 -0
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +16 -4
- package/dist/src/app/auth/authorizationManager.d.ts +0 -8
- package/dist/src/app/auth/authorizationManager.js +104 -178
- package/dist/src/app/auth/authorizationManager.js.map +1 -1
- package/dist/src/app/auth/handlerStorage.d.ts +5 -3
- package/dist/src/app/auth/handlerStorage.js +36 -10
- package/dist/src/app/auth/handlerStorage.js.map +1 -1
- package/dist/src/app/auth/types.d.ts +2 -2
- package/dist/src/app/index.d.ts +1 -0
- package/dist/src/app/proactive/proactive.js +10 -6
- package/dist/src/app/proactive/proactive.js.map +1 -1
- package/dist/src/app/proactive/proactiveOptions.d.ts +2 -2
- package/dist/src/app/rateLimit.d.ts +315 -0
- package/dist/src/app/rateLimit.js +333 -0
- package/dist/src/app/rateLimit.js.map +1 -0
- package/dist/src/app/turnState.d.ts +6 -3
- package/dist/src/app/turnState.js +43 -4
- package/dist/src/app/turnState.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +10 -3
- package/dist/src/auth/authConfiguration.js +292 -259
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/settings.d.ts +8 -70
- package/dist/src/auth/settings.js +5 -67
- package/dist/src/auth/settings.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +5 -0
- package/dist/src/cloudAdapter.js +13 -103
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/configuration/botFrameworkEnvironmentCompatibility.d.ts +23 -0
- package/dist/src/configuration/botFrameworkEnvironmentCompatibility.js +183 -0
- package/dist/src/configuration/botFrameworkEnvironmentCompatibility.js.map +1 -0
- package/dist/src/configuration/configuration.d.ts +83 -0
- package/dist/src/configuration/configuration.js +836 -0
- package/dist/src/configuration/configuration.js.map +1 -0
- package/dist/src/configuration/configurationSource.d.ts +57 -0
- package/dist/src/configuration/configurationSource.js +7 -0
- package/dist/src/configuration/configurationSource.js.map +1 -0
- package/dist/src/configuration/environmentConfiguration.d.ts +14 -0
- package/dist/src/configuration/environmentConfiguration.js +271 -0
- package/dist/src/configuration/environmentConfiguration.js.map +1 -0
- package/dist/src/configuration/index.d.ts +7 -0
- package/dist/src/configuration/index.js +14 -0
- package/dist/src/configuration/index.js.map +1 -0
- package/dist/src/configuration/jsonFileConfigurationSource.d.ts +15 -0
- package/dist/src/configuration/jsonFileConfigurationSource.js +58 -0
- package/dist/src/configuration/jsonFileConfigurationSource.js.map +1 -0
- package/dist/src/createCloudAdapter.d.ts +30 -5
- package/dist/src/createCloudAdapter.js +17 -10
- package/dist/src/createCloudAdapter.js.map +1 -1
- package/dist/src/errorHelper.js +117 -5
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/observability/traces.d.ts +6 -4
- package/dist/src/observability/traces.js +10 -9
- package/dist/src/observability/traces.js.map +1 -1
- package/dist/src/outboundHostValidator.d.ts +4 -1
- package/dist/src/outboundHostValidator.js +25 -48
- package/dist/src/outboundHostValidator.js.map +1 -1
- package/dist/src/state/agentState.d.ts +8 -3
- package/dist/src/state/agentState.js +26 -8
- package/dist/src/state/agentState.js.map +1 -1
- package/dist/src/state/conversationState.d.ts +2 -2
- package/dist/src/state/conversationState.js.map +1 -1
- package/dist/src/state/userState.d.ts +2 -2
- package/dist/src/state/userState.js.map +1 -1
- package/dist/src/statusCodes.d.ts +5 -0
- package/dist/src/statusCodes.js +5 -0
- package/dist/src/statusCodes.js.map +1 -1
- package/dist/src/storage/fileStorage.d.ts +83 -68
- package/dist/src/storage/fileStorage.js +279 -105
- package/dist/src/storage/fileStorage.js.map +1 -1
- package/dist/src/storage/index.d.ts +1 -0
- package/dist/src/storage/index.js +1 -0
- package/dist/src/storage/index.js.map +1 -1
- package/dist/src/storage/memoryStorage.d.ts +85 -60
- package/dist/src/storage/memoryStorage.js +244 -87
- package/dist/src/storage/memoryStorage.js.map +1 -1
- package/dist/src/storage/storage.d.ts +100 -2
- package/dist/src/storage/storage.js +25 -0
- package/dist/src/storage/storage.js.map +1 -1
- package/dist/src/storage/storageCompatibility.d.ts +17 -0
- package/dist/src/storage/storageCompatibility.js +157 -0
- package/dist/src/storage/storageCompatibility.js.map +1 -0
- package/dist/src/storage/storageExpiry.d.ts +6 -0
- package/dist/src/storage/storageExpiry.js +20 -0
- package/dist/src/storage/storageExpiry.js.map +1 -0
- package/dist/src/utils/env.d.ts +43 -0
- package/dist/src/utils/env.js +42 -6
- package/dist/src/utils/env.js.map +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +10 -1
- package/dist/src/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/app/agentApplication.ts +27 -14
- package/src/app/agentApplicationBuilder.ts +13 -2
- package/src/app/agentApplicationOptions.ts +18 -4
- package/src/app/auth/authorizationManager.ts +126 -204
- package/src/app/auth/handlerStorage.ts +51 -7
- package/src/app/auth/types.ts +2 -2
- package/src/app/index.ts +7 -0
- package/src/app/proactive/proactive.ts +19 -9
- package/src/app/proactive/proactiveOptions.ts +2 -2
- package/src/app/rateLimit.ts +659 -0
- package/src/app/turnState.ts +50 -7
- package/src/auth/authConfiguration.ts +415 -260
- package/src/auth/settings.ts +5 -82
- package/src/cloudAdapter.ts +38 -113
- package/src/configuration/botFrameworkEnvironmentCompatibility.ts +245 -0
- package/src/configuration/configuration.ts +1076 -0
- package/src/configuration/configurationSource.ts +76 -0
- package/src/configuration/environmentConfiguration.ts +377 -0
- package/src/configuration/index.ts +19 -0
- package/src/configuration/jsonFileConfigurationSource.ts +74 -0
- package/src/createCloudAdapter.ts +52 -9
- package/src/errorHelper.ts +143 -5
- package/src/index.ts +1 -0
- package/src/observability/traces.ts +8 -7
- package/src/outboundHostValidator.ts +37 -44
- package/src/state/agentState.ts +39 -13
- package/src/state/conversationState.ts +2 -2
- package/src/state/userState.ts +2 -2
- package/src/statusCodes.ts +6 -0
- package/src/storage/fileStorage.ts +299 -106
- package/src/storage/index.ts +1 -0
- package/src/storage/memoryStorage.ts +268 -85
- package/src/storage/storage.ts +114 -2
- package/src/storage/storageCompatibility.ts +185 -0
- package/src/storage/storageExpiry.ts +21 -0
- package/src/utils/env.ts +63 -0
- package/src/utils.ts +10 -0
package/src/auth/settings.ts
CHANGED
|
@@ -11,6 +11,8 @@ import type { SidecarConnectionSettings } from './sidecar/sidecarConnectionSetti
|
|
|
11
11
|
|
|
12
12
|
export type { MsalConnectionSettings } from './msal/msalConnectionSettings'
|
|
13
13
|
export type { SidecarConnectionSettings } from './sidecar/sidecarConnectionSettings'
|
|
14
|
+
export { envParser, envParserUtils, loadEnvSettings } from '../utils/env'
|
|
15
|
+
export type { LoadEnv } from '../utils/env'
|
|
14
16
|
|
|
15
17
|
const logger = debug('agents:authConfiguration')
|
|
16
18
|
|
|
@@ -32,25 +34,6 @@ export const DEFAULT_CONNECTION_MAP: ConnectionMapItem = { serviceUrl: '*', conn
|
|
|
32
34
|
export type ConnectionKeys = keyof Omit<AuthConfiguration, 'connections' | 'connectionsMap'>
|
|
33
35
|
export type ConnectionMapKeys = keyof ConnectionMapItem
|
|
34
36
|
|
|
35
|
-
export interface LoadEnv {
|
|
36
|
-
[upperKey: string]: { key: string, value: any }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function loadEnvSettings (callback: (key: string, value: string) => void) {
|
|
40
|
-
const env: LoadEnv = {}
|
|
41
|
-
for (const [envKey, envValue] of Object.entries(process.env)) {
|
|
42
|
-
if (!envValue?.trim()) {
|
|
43
|
-
continue
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
env[envKey.toUpperCase()] = { key: envKey, value: envValue }
|
|
47
|
-
|
|
48
|
-
callback(envKey, envValue)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return env
|
|
52
|
-
}
|
|
53
|
-
|
|
54
37
|
export function applyDefaultSettings (config: AuthConfiguration) {
|
|
55
38
|
const settings = { ...config }
|
|
56
39
|
settings.authorityEndpoint ??= settings.authority ?? AUTHORITY_DEFAULT
|
|
@@ -91,68 +74,6 @@ export function applyDefaultSettings (config: AuthConfiguration) {
|
|
|
91
74
|
return settings
|
|
92
75
|
}
|
|
93
76
|
|
|
94
|
-
/**
|
|
95
|
-
* A type representing a parser settings object.
|
|
96
|
-
*/
|
|
97
|
-
type ParserSettings<K extends string> = {
|
|
98
|
-
[key in K]: (value: string) => { key?: string, value?: any } | undefined
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Creates an environment variable parser that maps the variable keys to parsing functions.
|
|
103
|
-
* @param settings An object where each key is an environment variable name and the value is a function
|
|
104
|
-
* that takes the variable value as input and returns an object with optional `key` and `value` properties.
|
|
105
|
-
* @remarks
|
|
106
|
-
* The `key` property in the returned object can be used to rename the environment variable key,
|
|
107
|
-
* while the `value` property contains the parsed value.
|
|
108
|
-
* @returns An object with a `parse` method that takes an environment variable key and value,
|
|
109
|
-
* and returns the parsed result.
|
|
110
|
-
*/
|
|
111
|
-
export function envParser<K extends string> (settings: ParserSettings<K> & ThisType<ParserSettings<K>>) {
|
|
112
|
-
const keys = Object.keys(settings) as K[]
|
|
113
|
-
const upperKeys = keys.reduce((acc, key) => {
|
|
114
|
-
acc[key.toUpperCase()] = key
|
|
115
|
-
return acc
|
|
116
|
-
}, {} as Record<string, K>)
|
|
117
|
-
return {
|
|
118
|
-
keys,
|
|
119
|
-
/**
|
|
120
|
-
* Parses the given environment variable key and value using the provided settings.
|
|
121
|
-
* @param key The environment variable key.
|
|
122
|
-
* @param value The environment variable value.
|
|
123
|
-
* @returns The parsed result with optional renamed key and parsed value.
|
|
124
|
-
*/
|
|
125
|
-
parse (key: K, value: string) {
|
|
126
|
-
const match = upperKeys[key.toUpperCase()]
|
|
127
|
-
if (!match) {
|
|
128
|
-
return {}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const result = settings[match](value)
|
|
132
|
-
return { key: result?.key ?? match, value: result?.value }
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Utility functions for environment variable parsers.
|
|
139
|
-
*/
|
|
140
|
-
export const envParserUtils = {
|
|
141
|
-
/**
|
|
142
|
-
* Bypass parser that returns the value as is.
|
|
143
|
-
* @param value The environment variable value.
|
|
144
|
-
* @returns An object with the original value.
|
|
145
|
-
*/
|
|
146
|
-
bypass: (value: string) => ({ value }),
|
|
147
|
-
/**
|
|
148
|
-
* Redirects the parsing to another parser for a specific key.
|
|
149
|
-
* @param parser The target parser to redirect to.
|
|
150
|
-
* @param key The key to use in the target parser.
|
|
151
|
-
* @returns A function that takes the environment variable value and returns the parsed result from the target parser.
|
|
152
|
-
*/
|
|
153
|
-
redirect: <Parser extends ReturnType<typeof envParser>>(parser: Parser, key: Parameters<Parser['parse']>[0]) => (value: string) => parser.parse(key, value)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
77
|
/**
|
|
157
78
|
* Resolves the full authority URL including the tenant ID.
|
|
158
79
|
* Supports both patterns:
|
|
@@ -234,7 +155,9 @@ export interface ConnectionSettingsBase {
|
|
|
234
155
|
*
|
|
235
156
|
* @remarks
|
|
236
157
|
* Disabled by default for backward compatibility. Tenant-to-issuer binding is always applied
|
|
237
|
-
* independently when both claims contain comparable tenant GUIDs.
|
|
158
|
+
* independently when both claims contain comparable tenant GUIDs. Environment configuration
|
|
159
|
+
* accepts `true`, `false`, `1`, and `0` case-insensitively with surrounding whitespace ignored.
|
|
160
|
+
* Any other non-empty value fails configuration loading.
|
|
238
161
|
*/
|
|
239
162
|
validateIssuer?: boolean
|
|
240
163
|
|
package/src/cloudAdapter.ts
CHANGED
|
@@ -28,12 +28,17 @@ import { HeaderPropagation, HeaderPropagationCollection, HeaderPropagationDefini
|
|
|
28
28
|
import { JwtPayload } from 'jsonwebtoken'
|
|
29
29
|
import { getTokenServiceEndpoint } from './oauth/customUserTokenAPI'
|
|
30
30
|
import { Connections } from './auth/connections'
|
|
31
|
-
import { parseBooleanEnv, suggestClosest } from './utils/env'
|
|
32
31
|
import { trace } from '@microsoft/agents-telemetry'
|
|
33
32
|
import { AdapterTraceDefinitions } from './observability'
|
|
34
33
|
import { applyAgenticHeaders } from './getProductInfo'
|
|
35
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
createOutboundHostValidator,
|
|
36
|
+
OutboundUrlPolicy
|
|
37
|
+
} from './outboundHostValidator'
|
|
36
38
|
import { authorizeJWT } from './auth/jwt-middleware'
|
|
39
|
+
import { ConfigurationContext, getConfigurationSnapshot } from './configuration/configuration'
|
|
40
|
+
import { loadModernEnvironmentConfiguration } from './configuration/environmentConfiguration'
|
|
41
|
+
import { mergeDefined } from './utils'
|
|
37
42
|
|
|
38
43
|
const logger = debug('agents:cloud-adapter')
|
|
39
44
|
|
|
@@ -62,6 +67,11 @@ const logger = debug('agents:cloud-adapter')
|
|
|
62
67
|
* win over environment variables.
|
|
63
68
|
*/
|
|
64
69
|
export interface CloudAdapterOptions {
|
|
70
|
+
/**
|
|
71
|
+
* Optional host-scoped external configuration.
|
|
72
|
+
*/
|
|
73
|
+
configurationContext?: ConfigurationContext
|
|
74
|
+
|
|
65
75
|
/**
|
|
66
76
|
* When `true`, the default `onTurnError` handler includes `error.stack` in
|
|
67
77
|
* its log output. Defaults to `false`.
|
|
@@ -89,123 +99,35 @@ export interface CloudAdapterOptions {
|
|
|
89
99
|
validateServiceUrl?: boolean
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
type ResolvedCloudAdapterOptions = Required<Pick<CloudAdapterOptions, 'emitStackTrace' | 'validateServiceUrl'>>
|
|
103
|
+
|
|
104
|
+
const DEFAULT_CLOUD_ADAPTER_OPTIONS: ResolvedCloudAdapterOptions = {
|
|
93
105
|
emitStackTrace: false,
|
|
94
106
|
validateServiceUrl: false
|
|
95
107
|
}
|
|
96
108
|
|
|
97
|
-
/** Env-var prefix for {@link CloudAdapterOptions} (matches .NET config section). */
|
|
98
|
-
const CLOUD_ADAPTER_OPTIONS_ENV_PREFIX = 'CloudAdapterOptions__'
|
|
99
|
-
const CLOUD_ADAPTER_OPTIONS_ENV_PREFIX_UPPER = CLOUD_ADAPTER_OPTIONS_ENV_PREFIX.toUpperCase()
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Declarative env-var parser for {@link CloudAdapterOptions}.
|
|
103
|
-
*
|
|
104
|
-
* Shape mirrors `envParser<K>` introduced in PR #1119
|
|
105
|
-
* (`packages/agents-hosting/src/auth/settings.ts`) so this loader can be
|
|
106
|
-
* swapped for the shared utility once that lands. Each entry is a
|
|
107
|
-
* `(rawValue) => parsedValue | undefined` function. Lookups are
|
|
108
|
-
* case-insensitive via the `upperKeys` map, matching the upstream
|
|
109
|
-
* convention and accommodating hosts that uppercase env-var names.
|
|
110
|
-
*
|
|
111
|
-
* Adding a new option to `CloudAdapterOptions` only requires adding an
|
|
112
|
-
* entry here.
|
|
113
|
-
*/
|
|
114
|
-
const cloudAdapterOptionsParser = (() => {
|
|
115
|
-
const schema: {
|
|
116
|
-
[K in keyof Required<CloudAdapterOptions>]: (raw: string | undefined) => CloudAdapterOptions[K]
|
|
117
|
-
} = {
|
|
118
|
-
emitStackTrace: parseBooleanEnv,
|
|
119
|
-
validateServiceUrl: parseBooleanEnv
|
|
120
|
-
}
|
|
121
|
-
const keys = Object.keys(schema) as Array<keyof CloudAdapterOptions>
|
|
122
|
-
const upperKeys = keys.reduce<Record<string, keyof CloudAdapterOptions>>((acc, key) => {
|
|
123
|
-
acc[key.toUpperCase()] = key
|
|
124
|
-
return acc
|
|
125
|
-
}, {})
|
|
126
|
-
return {
|
|
127
|
-
schema,
|
|
128
|
-
keys,
|
|
129
|
-
/**
|
|
130
|
-
* Resolves an env-var property name (case-insensitive) to its canonical
|
|
131
|
-
* `CloudAdapterOptions` key, or `undefined` when the name is unknown.
|
|
132
|
-
*/
|
|
133
|
-
resolveKey (property: string): keyof CloudAdapterOptions | undefined {
|
|
134
|
-
return upperKeys[property.toUpperCase()]
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
})()
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Per-process dedup set for configuration warnings. Without this, every
|
|
141
|
-
* `new CloudAdapter()` re-scans `process.env` and re-emits warnings for any
|
|
142
|
-
* typo'd `CloudAdapterOptions__*` key (or unparseable value), which spams
|
|
143
|
-
* stderr in multi-adapter scenarios (tests, proactive flows, DI containers).
|
|
144
|
-
*/
|
|
145
|
-
const warnedConfigKeys = new Set<string>()
|
|
146
|
-
|
|
147
|
-
function emitConfigWarning (envKey: string, message: string): void {
|
|
148
|
-
if (warnedConfigKeys.has(envKey)) return
|
|
149
|
-
warnedConfigKeys.add(envKey)
|
|
150
|
-
// Visible by default (writes synchronously to stderr) so users see typos
|
|
151
|
-
// and bad values without having to opt in via `DEBUG=agents:cloud-adapter:*`.
|
|
152
|
-
// Hosts that want to route or suppress can intercept `console.warn` or
|
|
153
|
-
// subscribe to the `agents:cloud-adapter:warn` debug namespace below.
|
|
154
|
-
console.warn(`[agents:cloud-adapter] ${message}`)
|
|
155
|
-
logger.warn(message)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Scans `process.env` for keys with the `CloudAdapterOptions__` prefix
|
|
160
|
-
* (case-insensitive) and returns the parsed partial options. Unknown keys
|
|
161
|
-
* and values that fail to parse are warned about once per process via
|
|
162
|
-
* `console.warn` (so they are visible by default without enabling debug)
|
|
163
|
-
* and through the `agents:cloud-adapter:warn` debug channel for log
|
|
164
|
-
* aggregators. Hosts that want to route or suppress these diagnostics can
|
|
165
|
-
* intercept `console.warn` or filter the debug namespace.
|
|
166
|
-
*/
|
|
167
|
-
function loadCloudAdapterOptionsFromEnv (): CloudAdapterOptions {
|
|
168
|
-
const result: CloudAdapterOptions = {}
|
|
169
|
-
for (const [envKey, rawValue] of Object.entries(process.env)) {
|
|
170
|
-
const upper = envKey.toUpperCase()
|
|
171
|
-
if (!upper.startsWith(CLOUD_ADAPTER_OPTIONS_ENV_PREFIX_UPPER)) continue
|
|
172
|
-
const property = envKey.substring(CLOUD_ADAPTER_OPTIONS_ENV_PREFIX.length)
|
|
173
|
-
const canonical = cloudAdapterOptionsParser.resolveKey(property)
|
|
174
|
-
if (!canonical) {
|
|
175
|
-
const suggestion = suggestClosest(property, cloudAdapterOptionsParser.keys as readonly string[], 4)
|
|
176
|
-
const hint = suggestion ? ` Did you mean "${CLOUD_ADAPTER_OPTIONS_ENV_PREFIX}${suggestion}"?` : ''
|
|
177
|
-
emitConfigWarning(envKey, `Unknown CloudAdapterOptions env var: ${envKey} (ignored).${hint}`)
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
|
-
const parsed = cloudAdapterOptionsParser.schema[canonical](rawValue) as any
|
|
181
|
-
if (parsed !== undefined) {
|
|
182
|
-
(result as any)[canonical] = parsed
|
|
183
|
-
} else if (rawValue !== undefined && rawValue.trim() !== '') {
|
|
184
|
-
// Known key, recognized but unparseable value (e.g. `yes`, `on`, `enabled`).
|
|
185
|
-
// For a security-relevant flag like `validateServiceUrl`, silent
|
|
186
|
-
// fallthrough is the dangerous failure mode — surface it.
|
|
187
|
-
// Note: parseBooleanEnv treats whitespace-only as unset, so don't warn
|
|
188
|
-
// on `' '`; only warn when the user actually typed something.
|
|
189
|
-
emitConfigWarning(
|
|
190
|
-
`${envKey}=${rawValue}`,
|
|
191
|
-
`Ignored ${envKey}=${rawValue}; expected one of true/false/1/0.`
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return result
|
|
196
|
-
}
|
|
197
|
-
|
|
198
109
|
/**
|
|
199
110
|
* Resolves a `CloudAdapterOptions` instance from an explicit argument or, if
|
|
200
111
|
* absent, from environment variables. Values supplied in the explicit object
|
|
201
112
|
* win over env vars.
|
|
202
113
|
*/
|
|
203
|
-
function resolveCloudAdapterOptions (options?: CloudAdapterOptions):
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
114
|
+
function resolveCloudAdapterOptions (options?: CloudAdapterOptions): ResolvedCloudAdapterOptions {
|
|
115
|
+
const external = getConfigurationSnapshot(options?.configurationContext)
|
|
116
|
+
const fromEnv = loadModernEnvironmentConfiguration(
|
|
117
|
+
process.env,
|
|
118
|
+
{ reportCloudAdapterDiagnostics: true }
|
|
119
|
+
).cloudAdapterOptions
|
|
120
|
+
return mergeDefined<ResolvedCloudAdapterOptions>(
|
|
121
|
+
DEFAULT_CLOUD_ADAPTER_OPTIONS,
|
|
122
|
+
external.fallback.cloudAdapterOptions,
|
|
123
|
+
fromEnv,
|
|
124
|
+
external.overrideEnvironment.cloudAdapterOptions,
|
|
125
|
+
{
|
|
126
|
+
emitStackTrace: options?.emitStackTrace,
|
|
127
|
+
validateServiceUrl: options?.validateServiceUrl
|
|
128
|
+
},
|
|
129
|
+
external.enforce.cloudAdapterOptions
|
|
130
|
+
)
|
|
209
131
|
}
|
|
210
132
|
|
|
211
133
|
/**
|
|
@@ -251,7 +173,7 @@ export class CloudAdapter extends BaseAdapter {
|
|
|
251
173
|
*/
|
|
252
174
|
connectionManager: Connections
|
|
253
175
|
|
|
254
|
-
private readonly _options:
|
|
176
|
+
private readonly _options: ResolvedCloudAdapterOptions
|
|
255
177
|
private readonly _hostValidator: OutboundUrlPolicy
|
|
256
178
|
|
|
257
179
|
/**
|
|
@@ -266,11 +188,14 @@ export class CloudAdapter extends BaseAdapter {
|
|
|
266
188
|
*/
|
|
267
189
|
constructor (authConfig?: AuthConfiguration, authProvider?: AuthProvider, userTokenClient?: UserTokenClient, options?: CloudAdapterOptions, outboundHostValidator?: OutboundUrlPolicy) {
|
|
268
190
|
super()
|
|
269
|
-
this.authConfig = authConfig = getAuthConfigWithDefaults(authConfig
|
|
191
|
+
this.authConfig = authConfig = getAuthConfigWithDefaults(authConfig, {
|
|
192
|
+
configurationContext: options?.configurationContext
|
|
193
|
+
})
|
|
270
194
|
this.jwtMiddleware = authorizeJWT(authConfig)
|
|
271
195
|
this.connectionManager = new MsalConnectionManager(undefined, undefined, authConfig)
|
|
272
196
|
this._options = resolveCloudAdapterOptions(options)
|
|
273
|
-
this._hostValidator = outboundHostValidator ??
|
|
197
|
+
this._hostValidator = outboundHostValidator ??
|
|
198
|
+
createOutboundHostValidator({ configurationContext: options?.configurationContext })
|
|
274
199
|
|
|
275
200
|
// Install a CloudAdapter-aware default `onTurnError` that honors
|
|
276
201
|
// `emitStackTrace`. The base class default only logs the message; we
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ConfigurationLayer,
|
|
8
|
+
createConfigurationLayer,
|
|
9
|
+
freezeConfigurationLayer,
|
|
10
|
+
isConfigurationInputError,
|
|
11
|
+
isInvalidConfigurationValueError,
|
|
12
|
+
setConfigurationValue
|
|
13
|
+
} from './configuration'
|
|
14
|
+
import { LoadEnv, loadEnvSettings } from '../utils/env'
|
|
15
|
+
|
|
16
|
+
const DEFAULT_CONNECTION_ID = 'serviceConnection'
|
|
17
|
+
|
|
18
|
+
const flatConnectionProperties = {
|
|
19
|
+
MicrosoftAppTenantId: 'tenantId',
|
|
20
|
+
MicrosoftAppId: 'clientId',
|
|
21
|
+
MicrosoftAppPassword: 'clientSecret',
|
|
22
|
+
certPemFile: 'certPemFile',
|
|
23
|
+
certKeyFile: 'certKeyFile',
|
|
24
|
+
connectionName: 'connectionName',
|
|
25
|
+
MicrosoftAppClientId: 'federatedClientId',
|
|
26
|
+
authorityEndpoint: 'authorityEndpoint',
|
|
27
|
+
scope: 'scopes',
|
|
28
|
+
altBlueprintConnectionName: 'altBlueprintConnectionName',
|
|
29
|
+
alternateBlueprintConnectionName: 'altBlueprintConnectionName',
|
|
30
|
+
WIDAssertionFile: 'WIDAssertionFile',
|
|
31
|
+
azureRegion: 'azureRegion',
|
|
32
|
+
sendX5C: 'sendX5C',
|
|
33
|
+
msalRetryCount: 'msalRetryCount',
|
|
34
|
+
authType: 'authType',
|
|
35
|
+
federatedTokenFile: 'federatedTokenFile',
|
|
36
|
+
idpmResource: 'idpmResource',
|
|
37
|
+
sidecarBaseUrl: 'sidecarBaseUrl',
|
|
38
|
+
serviceName: 'serviceName',
|
|
39
|
+
blueprintServiceName: 'blueprintServiceName',
|
|
40
|
+
bypassLocalNetworkRestriction: 'bypassLocalNetworkRestriction',
|
|
41
|
+
requestTimeout: 'requestTimeout',
|
|
42
|
+
retryCount: 'retryCount',
|
|
43
|
+
validateIssuer: 'validateIssuer'
|
|
44
|
+
} as const
|
|
45
|
+
|
|
46
|
+
const prefixedConnectionProperties = {
|
|
47
|
+
tenantId: 'tenantId',
|
|
48
|
+
clientId: 'clientId',
|
|
49
|
+
clientSecret: 'clientSecret',
|
|
50
|
+
certPemFile: 'certPemFile',
|
|
51
|
+
certKeyFile: 'certKeyFile',
|
|
52
|
+
connectionName: 'connectionName',
|
|
53
|
+
FICClientId: 'federatedClientId',
|
|
54
|
+
authorityEndpoint: 'authorityEndpoint',
|
|
55
|
+
scope: 'scopes',
|
|
56
|
+
altBlueprintConnectionName: 'altBlueprintConnectionName',
|
|
57
|
+
alternateBlueprintConnectionName: 'altBlueprintConnectionName',
|
|
58
|
+
WIDAssertionFile: 'WIDAssertionFile',
|
|
59
|
+
azureRegion: 'azureRegion',
|
|
60
|
+
sendX5C: 'sendX5C',
|
|
61
|
+
msalRetryCount: 'msalRetryCount',
|
|
62
|
+
authType: 'authType',
|
|
63
|
+
federatedTokenFile: 'federatedTokenFile',
|
|
64
|
+
idpmResource: 'idpmResource',
|
|
65
|
+
sidecarBaseUrl: 'sidecarBaseUrl',
|
|
66
|
+
serviceName: 'serviceName',
|
|
67
|
+
blueprintServiceName: 'blueprintServiceName',
|
|
68
|
+
bypassLocalNetworkRestriction: 'bypassLocalNetworkRestriction',
|
|
69
|
+
requestTimeout: 'requestTimeout',
|
|
70
|
+
retryCount: 'retryCount',
|
|
71
|
+
validateIssuer: 'validateIssuer'
|
|
72
|
+
} as const
|
|
73
|
+
|
|
74
|
+
const legacyAuthorizationProperties = {
|
|
75
|
+
type: 'type',
|
|
76
|
+
connectionName: 'azureBotOAuthConnectionName',
|
|
77
|
+
connectionTitle: 'title',
|
|
78
|
+
connectionText: 'text',
|
|
79
|
+
maxAttempts: 'invalidSignInRetryMax',
|
|
80
|
+
messages_invalidCode: 'invalidSignInRetryMessage',
|
|
81
|
+
messages_invalidCodeFormat: 'invalidSignInRetryMessageFormat',
|
|
82
|
+
messages_maxAttemptsExceeded: 'invalidSignInRetryMaxExceededMessage',
|
|
83
|
+
obo_connection: 'oboConnectionName',
|
|
84
|
+
obo_scopes: 'oboScopes',
|
|
85
|
+
enableSso: 'enableSso',
|
|
86
|
+
scopes: 'scopes',
|
|
87
|
+
altBlueprintConnectionName: 'altBlueprintConnectionName'
|
|
88
|
+
} as const
|
|
89
|
+
|
|
90
|
+
interface BotFrameworkAuthorizationCompatibility {
|
|
91
|
+
readonly layer: ConfigurationLayer
|
|
92
|
+
readonly replacements: readonly Readonly<{
|
|
93
|
+
legacyKey: string
|
|
94
|
+
modernKey: string
|
|
95
|
+
}>[]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Binds the flat Bot Framework single-connection environment variables. */
|
|
99
|
+
export function loadBotFrameworkEnvironmentConfiguration (
|
|
100
|
+
env: NodeJS.ProcessEnv = process.env
|
|
101
|
+
): ConfigurationLayer {
|
|
102
|
+
return bindConnectionCompatibility(env, undefined, flatConnectionProperties)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Binds the Bot Framework `<connectionName>_<property>` environment form. */
|
|
106
|
+
export function loadBotFrameworkPrefixedEnvironmentConfiguration (
|
|
107
|
+
connectionName: string,
|
|
108
|
+
env: NodeJS.ProcessEnv = process.env
|
|
109
|
+
): ConfigurationLayer {
|
|
110
|
+
return bindConnectionCompatibility(env, connectionName, prefixedConnectionProperties)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Binds Bot Framework-era `<handlerId>_<property>` authorization variables.
|
|
115
|
+
* Only known runtime/external handler IDs participate, matching existing
|
|
116
|
+
* compatibility behavior.
|
|
117
|
+
*/
|
|
118
|
+
export function loadBotFrameworkAuthorizationEnvironmentConfiguration (
|
|
119
|
+
handlerIds: readonly string[],
|
|
120
|
+
env: NodeJS.ProcessEnv = process.env
|
|
121
|
+
): BotFrameworkAuthorizationCompatibility {
|
|
122
|
+
const layer = createConfigurationLayer()
|
|
123
|
+
const replacements: Array<{ legacyKey: string, modernKey: string }> = []
|
|
124
|
+
const longestHandlerIdsFirst = [...handlerIds].sort((left, right) => right.length - left.length)
|
|
125
|
+
|
|
126
|
+
for (const [legacyKey, rawValue] of Object.entries(env)) {
|
|
127
|
+
if (!rawValue?.trim()) {
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
const id = longestHandlerIdsFirst.find(candidate =>
|
|
131
|
+
legacyKey.toLowerCase().startsWith(`${candidate.toLowerCase()}_`)
|
|
132
|
+
)
|
|
133
|
+
if (!id) {
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const legacyProperty = legacyKey.substring(id.length + 1)
|
|
138
|
+
const property = findProperty(legacyAuthorizationProperties, legacyProperty)
|
|
139
|
+
if (!property) {
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const value = normalizeAuthorizationValue(property, rawValue)
|
|
144
|
+
if (!trySet(
|
|
145
|
+
layer,
|
|
146
|
+
`agentApplication.userAuthorization.handlers.${id}.settings.${property}`,
|
|
147
|
+
value,
|
|
148
|
+
legacyKey
|
|
149
|
+
)) {
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
replacements.push({
|
|
154
|
+
legacyKey,
|
|
155
|
+
modernKey: `AgentApplication__UserAuthorization__Handlers__${id}__Settings__${property}`
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
layer: freezeConfigurationLayer(layer),
|
|
161
|
+
replacements: Object.freeze(replacements.map(replacement => Object.freeze(replacement)))
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function bindConnectionCompatibility (
|
|
166
|
+
env: NodeJS.ProcessEnv,
|
|
167
|
+
connectionName: string | undefined,
|
|
168
|
+
properties: Readonly<Record<string, string>>
|
|
169
|
+
): ConfigurationLayer {
|
|
170
|
+
const layer = createConfigurationLayer()
|
|
171
|
+
const id = connectionName || DEFAULT_CONNECTION_ID
|
|
172
|
+
const indexedEnvironment = loadEnvSettings(() => {}, env)
|
|
173
|
+
|
|
174
|
+
for (const [legacyProperty, property] of Object.entries(properties)) {
|
|
175
|
+
const key = connectionName ? `${connectionName}_${legacyProperty}` : legacyProperty
|
|
176
|
+
const value = findEnvironmentValue(indexedEnvironment, key)
|
|
177
|
+
if (!value?.trim()) {
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
trySet(
|
|
181
|
+
layer,
|
|
182
|
+
`connections.${id}.settings.${property}`,
|
|
183
|
+
normalizeConnectionValue(property, value),
|
|
184
|
+
key,
|
|
185
|
+
property === 'validateIssuer'
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return freezeConfigurationLayer(layer)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function findEnvironmentValue (env: LoadEnv, key: string): string | undefined {
|
|
193
|
+
return env[key.toUpperCase()]?.value
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function findProperty (
|
|
197
|
+
properties: Readonly<Record<string, string>>,
|
|
198
|
+
input: string
|
|
199
|
+
): string | undefined {
|
|
200
|
+
const key = Object.keys(properties).find(candidate => candidate.toLowerCase() === input.toLowerCase())
|
|
201
|
+
return key ? properties[key] : undefined
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeConnectionValue (property: string, value: string): string {
|
|
205
|
+
if (property === 'sendX5C' || property === 'bypassLocalNetworkRestriction') {
|
|
206
|
+
return String(value === 'true')
|
|
207
|
+
}
|
|
208
|
+
if (property === 'msalRetryCount' || property === 'requestTimeout' || property === 'retryCount') {
|
|
209
|
+
const parsed = Number.parseInt(value, 10)
|
|
210
|
+
return Number.isFinite(parsed) ? String(parsed) : value
|
|
211
|
+
}
|
|
212
|
+
return value
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function normalizeAuthorizationValue (property: string, value: string): string {
|
|
216
|
+
if (property === 'enableSso') {
|
|
217
|
+
return String(value !== 'false')
|
|
218
|
+
}
|
|
219
|
+
if (property === 'invalidSignInRetryMax') {
|
|
220
|
+
const parsed = Number.parseInt(value)
|
|
221
|
+
return Number.isFinite(parsed) ? String(parsed) : value
|
|
222
|
+
}
|
|
223
|
+
return value
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function trySet (
|
|
227
|
+
layer: ConfigurationLayer,
|
|
228
|
+
path: string,
|
|
229
|
+
value: string,
|
|
230
|
+
sourceName: string,
|
|
231
|
+
throwOnInvalidValue = false
|
|
232
|
+
): boolean {
|
|
233
|
+
try {
|
|
234
|
+
setConfigurationValue(layer, path, value, sourceName, 'environment')
|
|
235
|
+
return true
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (!isConfigurationInputError(error)) {
|
|
238
|
+
throw error
|
|
239
|
+
}
|
|
240
|
+
if (throwOnInvalidValue && isInvalidConfigurationValueError(error)) {
|
|
241
|
+
throw error
|
|
242
|
+
}
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
}
|