diff --git a/libraries/botbuilder-dialogs-adaptive-runtime/src/index.ts b/libraries/botbuilder-dialogs-adaptive-runtime/src/index.ts index 7a9c34eff1..26987bbfe0 100644 --- a/libraries/botbuilder-dialogs-adaptive-runtime/src/index.ts +++ b/libraries/botbuilder-dialogs-adaptive-runtime/src/index.ts @@ -229,10 +229,34 @@ function addSkills(services: ServiceCollection, configuration: Configuration): v const allowedCallers = configuration.type(['runtimeSettings', 'skills', 'allowedCallers'], z.array(z.string())) ?? []; - return new AuthenticationConfiguration( - undefined, - allowedCallers.length ? allowedCallersClaimsValidator(allowedCallers) : undefined + const skills = Object.values( + configuration.type( + ['skills'], + z.record( + z + .object({ + msAppId: z.string(), + }) + .nonstrict() + ) + ) ?? {} ); + + if (skills.length) { + // If the config entry for "skills" is present then we are a consumer and the entries under + // runtimeSettings.sills are ignored + return new AuthenticationConfiguration( + undefined, + allowedCallersClaimsValidator(skills.map((skill) => skill.msAppId)) + ); + } else { + // If the config entry for runtimeSettings.skills.allowedCallers contains entries, then we are a skill and + // we validate caller against this list + return new AuthenticationConfiguration( + undefined, + allowedCallers.length ? allowedCallersClaimsValidator(allowedCallers) : undefined + ); + } }); services.addFactory< diff --git a/libraries/botbuilder-dialogs-adaptive-runtime/test/index.test.ts b/libraries/botbuilder-dialogs-adaptive-runtime/test/index.test.ts index f8f1779537..f0047df6f8 100644 --- a/libraries/botbuilder-dialogs-adaptive-runtime/test/index.test.ts +++ b/libraries/botbuilder-dialogs-adaptive-runtime/test/index.test.ts @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import sinon from 'sinon'; +import { AuthenticationConfiguration, AuthenticationConstants, SkillValidation } from 'botframework-connector'; import { BlobsStorage } from 'botbuilder-azure-blobs'; import { BotComponent, BotFrameworkAdapter, MemoryStorage } from 'botbuilder'; import { Configuration, getRuntimeServices } from '../src'; import { CosmosDbPartitionedStorage } from 'botbuilder-azure'; -import { ok, strictEqual } from 'assert'; import { ServiceCollection, Configuration as CoreConfiguration } from 'botbuilder-dialogs-adaptive-runtime-core'; +import { ok, rejects, strictEqual } from 'assert'; describe('getRuntimeServices', function () { it('works', async function () { @@ -109,4 +111,93 @@ describe('getRuntimeServices', function () { ok(storage instanceof CosmosDbPartitionedStorage); }); }); + + describe('skills', function () { + let sandbox: sinon.SinonSandbox; + beforeEach(function () { + sandbox = sinon.createSandbox(); + sandbox.stub(SkillValidation, 'isSkillClaim').returns(true); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('supports .runtimeSettings.skills', async function () { + const configuration = new Configuration(); + + configuration.set(['runtimeSettings', 'skills'], { + allowedCallers: ['AppId'], + }); + + const [services] = await getRuntimeServices(__dirname, configuration); + const authenticationConfiguration = services.mustMakeInstance( + 'authenticationConfiguration' + ); + + const { validateClaims } = authenticationConfiguration; + ok(validateClaims); + + await validateClaims([ + { + type: AuthenticationConstants.AppIdClaim, + value: 'AppId', + }, + ]); + + await rejects( + validateClaims([ + { + type: AuthenticationConstants.AppIdClaim, + value: 'BadAppId', + }, + ]) + ); + }); + + it('supports .skills', async function () { + const configuration = new Configuration(); + + configuration.set(['skills'], { + a: { + msAppId: 'AppA', + }, + b: { + msAppId: 'AppB', + }, + }); + + const [services] = await getRuntimeServices(__dirname, configuration); + const authenticationConfiguration = services.mustMakeInstance( + 'authenticationConfiguration' + ); + + const { validateClaims } = authenticationConfiguration; + ok(validateClaims); + + await Promise.all([ + validateClaims([ + { + type: AuthenticationConstants.AppIdClaim, + value: 'AppA', + }, + ]), + validateClaims([ + { + type: AuthenticationConstants.AppIdClaim, + value: 'AppB', + }, + ]), + ]); + + await rejects( + validateClaims([ + { + type: AuthenticationConstants.AppIdClaim, + value: 'AppC', + }, + ]) + ); + }); + }); });