diff --git a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/SkillConversationIdFactory.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/SkillConversationIdFactory.java similarity index 55% rename from samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/SkillConversationIdFactory.java rename to libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/SkillConversationIdFactory.java index f9e8cb23c..bee7c0fe1 100644 --- a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/SkillConversationIdFactory.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/SkillConversationIdFactory.java @@ -1,16 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MT License. -package com.microsoft.bot.sample.dialogrootbot; +package com.microsoft.bot.builder.skills; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import com.microsoft.bot.builder.Storage; -import com.microsoft.bot.builder.skills.SkillConversationIdFactoryBase; -import com.microsoft.bot.builder.skills.SkillConversationIdFactoryOptions; -import com.microsoft.bot.builder.skills.SkillConversationReference; import com.microsoft.bot.connector.Async; import com.microsoft.bot.schema.ConversationReference; @@ -25,6 +22,11 @@ public class SkillConversationIdFactory extends SkillConversationIdFactoryBase { private Storage storage; + /** + * Creates an instance of a SkillConversationIdFactory. + * + * @param storage A storage instance for the factory. + */ public SkillConversationIdFactory(Storage storage) { if (storage == null) { throw new IllegalArgumentException("Storage cannot be null."); @@ -32,18 +34,26 @@ public SkillConversationIdFactory(Storage storage) { this.storage = storage; } + /** + * Creates a conversation id for a skill conversation. + * + * @param options A {@link SkillConversationIdFactoryOptions} instance + * containing parameters for creating the conversation ID. + * + * @return A unique conversation ID used to communicate with the skill. + * + * It should be possible to use the returned String on a request URL and + * it should not contain special characters. + */ @Override public CompletableFuture createSkillConversationId(SkillConversationIdFactoryOptions options) { if (options == null) { Async.completeExceptionally(new IllegalArgumentException("options cannot be null.")); } ConversationReference conversationReference = options.getActivity().getConversationReference(); - String skillConversationId = String.format( - "%s-%s-%s-skillconvo", - conversationReference.getConversation().getId(), - options.getBotFrameworkSkill().getId(), - conversationReference.getChannelId() - ); + String skillConversationId = String.format("%s-%s-%s-skillconvo", + conversationReference.getConversation().getId(), options.getBotFrameworkSkill().getId(), + conversationReference.getChannelId()); SkillConversationReference skillConversationReference = new SkillConversationReference(); skillConversationReference.setConversationReference(conversationReference); @@ -51,9 +61,20 @@ public CompletableFuture createSkillConversationId(SkillConversationIdFa Map skillConversationInfo = new HashMap(); skillConversationInfo.put(skillConversationId, skillConversationReference); return storage.write(skillConversationInfo) - .thenCompose(result -> CompletableFuture.completedFuture(skillConversationId)); + .thenCompose(result -> CompletableFuture.completedFuture(skillConversationId)); } + /** + * Gets the {@link SkillConversationReference} created using + * {@link SkillConversationIdFactory#createSkillConversationId} for a + * skillConversationId. + * + * @param skillConversationId A skill conversationId created using + * {@link SkillConversationIdFactory#createSkillConversationId}. + * + * @return The caller's {@link ConversationReference} for a skillConversationId. + * null if not found. + */ @Override public CompletableFuture getSkillConversationReference(String skillConversationId) { if (StringUtils.isAllBlank(skillConversationId)) { @@ -63,13 +84,22 @@ public CompletableFuture getSkillConversationReferen return storage.read(new String[] {skillConversationId}).thenCompose(skillConversationInfo -> { if (skillConversationInfo.size() > 0) { return CompletableFuture - .completedFuture((SkillConversationReference) skillConversationInfo.get(skillConversationId)); + .completedFuture((SkillConversationReference) skillConversationInfo.get(skillConversationId)); } else { return CompletableFuture.completedFuture(null); } }); } + /** + * Deletes a {@link ConversationReference} . + * + * @param skillConversationId A skill conversationId created using {@link + * CreateSkillConversationId(SkillConversationIdFactoryOptions,System#getT + * reading()#getCancellationToken())} . + * + * @return A {@link CompletableFuture} representing the asynchronous operation. + */ @Override public CompletableFuture deleteConversationReference(String skillConversationId) { return storage.delete(new String[] {skillConversationId}); diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/SkillConversationIdFactoryTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/SkillConversationIdFactoryTests.java new file mode 100644 index 000000000..2ca4d4625 --- /dev/null +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/SkillConversationIdFactoryTests.java @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MT License. + +package com.microsoft.bot.builder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.UUID; + +import com.microsoft.bot.builder.skills.BotFrameworkSkill; +import com.microsoft.bot.builder.skills.SkillConversationIdFactory; +import com.microsoft.bot.builder.skills.SkillConversationIdFactoryOptions; +import com.microsoft.bot.builder.skills.SkillConversationReference; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.ConversationAccount; +import com.microsoft.bot.schema.ConversationReference; + +import org.junit.Test; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; + + +public class SkillConversationIdFactoryTests { + + private static final String SERVICE_URL = "http://testbot.com/api/messages"; + private final String skillId = "skill"; + + private final SkillConversationIdFactory skillConversationIdFactory = + new SkillConversationIdFactory(new MemoryStorage()); + private final String applicationId = UUID.randomUUID().toString(); + private final String botId = UUID.randomUUID().toString(); + + @Test + public void SkillConversationIdFactoryHappyPath() { + ConversationReference conversationReference = buildConversationReference(); + + // Create skill conversation + SkillConversationIdFactoryOptions options = new SkillConversationIdFactoryOptions(); + options.setActivity(buildMessageActivity(conversationReference)); + options.setBotFrameworkSkill(this.buildBotFrameworkSkill()); + options.setFromBotId(botId); + options.setFromBotOAuthScope(botId); + + + String skillConversationId = skillConversationIdFactory.createSkillConversationId(options).join(); + + Assert.assertFalse(StringUtils.isBlank(skillConversationId)); + + // Retrieve skill conversation + SkillConversationReference retrievedConversationReference = + skillConversationIdFactory.getSkillConversationReference(skillConversationId).join(); + + // Delete + skillConversationIdFactory.deleteConversationReference(skillConversationId); + + // Retrieve again + SkillConversationReference deletedConversationReference = + skillConversationIdFactory.getSkillConversationReference(skillConversationId).join(); + + Assert.assertNotNull(retrievedConversationReference); + Assert.assertNotNull(retrievedConversationReference.getConversationReference()); + Assert.assertTrue(compareConversationReferences(conversationReference, + retrievedConversationReference.getConversationReference())); + Assert.assertNull(deletedConversationReference); + } + + private static ConversationReference buildConversationReference() { + ConversationReference conversationReference = new ConversationReference(); + conversationReference.setConversation(new ConversationAccount(UUID.randomUUID().toString())); + conversationReference.setServiceUrl(SERVICE_URL); + return conversationReference; + } + + private static Activity buildMessageActivity(ConversationReference conversationReference) { + if (conversationReference == null) { + throw new IllegalArgumentException("conversationReference cannot be null."); + } + + Activity activity = Activity.createMessageActivity(); + activity.applyConversationReference(conversationReference); + + return activity; + } + + private BotFrameworkSkill buildBotFrameworkSkill() { + BotFrameworkSkill skill = new BotFrameworkSkill(); + skill.setAppId(applicationId); + skill.setId(skillId); + try { + skill.setSkillEndpoint(new URI(SERVICE_URL)); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return skill; + } + + private static boolean compareConversationReferences( + ConversationReference reference1, + ConversationReference reference2 + ) { + return reference1.getConversation().getId() == reference2.getConversation().getId() + && reference1.getServiceUrl() == reference2.getServiceUrl(); + } +} diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java new file mode 100644 index 000000000..da3eff071 --- /dev/null +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MT License. + +package com.microsoft.bot.connector.authentication; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.bot.connector.Async; + +/** + * Sample claims validator that loads an allowed list from configuration if + * presentand checks that requests are coming from allowed parent bots. + */ +public class AllowedCallersClaimsValidator extends ClaimsValidator { + + private List allowedCallers; + + /** + * Creates an instance of an {@link AllowedCallersClaimsValidator}. + * @param withAllowedCallers A List that contains the list of allowed callers. + */ + public AllowedCallersClaimsValidator(List withAllowedCallers) { + this.allowedCallers = withAllowedCallers != null ? withAllowedCallers : new ArrayList(); + } + + /** + * Validates a Map of claims and should throw an exception if the + * validation fails. + * + * @param claims The Map of claims to validate. + * + * @return true if the validation is successful, false if not. + */ + @Override + public CompletableFuture validateClaims(Map claims) { + if (claims == null) { + return Async.completeExceptionally(new IllegalArgumentException("Claims cannot be null")); + } + + // If _allowedCallers contains an "*", we allow all callers. + if (SkillValidation.isSkillClaim(claims) && !allowedCallers.contains("*")) { + // Check that the appId claim in the skill request instanceof in the list of + // callers configured for this bot. + String appId = JwtTokenValidation.getAppIdFromClaims(claims); + if (!allowedCallers.contains(appId)) { + return Async.completeExceptionally( + new RuntimeException( + String.format( + "Received a request from a bot with an app ID of \"%s\". To enable requests from this " + + "caller, add the app ID to the configured set of allowedCallers.", + appId + ) + ) + ); + } + } + + return CompletableFuture.completedFuture(null); + } +} diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/AllowedCallersClaimsValidationTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/AllowedCallersClaimsValidationTests.java new file mode 100644 index 000000000..db50423ed --- /dev/null +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/AllowedCallersClaimsValidationTests.java @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MT License. + +package com.microsoft.bot.connector; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.microsoft.bot.connector.authentication.AllowedCallersClaimsValidator; +import com.microsoft.bot.connector.authentication.AuthenticationConstants; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Assert; +import org.junit.Test; + +public class AllowedCallersClaimsValidationTests { + + private final String version = "1.0"; + + private final String audienceClaim = UUID.randomUUID().toString(); + + public static List>> getConfigureServicesSucceedsData() { + String primaryAppId = UUID.randomUUID().toString(); + String secondaryAppId = UUID.randomUUID().toString(); + + List>> resultList = new ArrayList>>(); + // Null allowed callers + resultList.add(Pair.of(null, null)); + // Null configuration with attempted caller + resultList.add(Pair.of(primaryAppId, null)); + // Empty allowed callers array + resultList.add(Pair.of(null, new ArrayList())); + // Allow any caller + resultList.add(Pair.of(primaryAppId, new ArrayList() { { add("*"); } })); + // Specify allowed caller + resultList.add((Pair.of(primaryAppId, new ArrayList() { { add(primaryAppId); } }))); + // Specify multiple callers + resultList.add((Pair.of(primaryAppId, new ArrayList() { { add(primaryAppId); + add(secondaryAppId); } }))); + // Blocked caller throws exception + resultList.add((Pair.of(primaryAppId, new ArrayList() { { add(secondaryAppId); } }))); + return resultList; + } + + @Test + public void TestAcceptAllowedCallersArray() { + List>> configuredServices = getConfigureServicesSucceedsData(); + for (Pair> item : configuredServices) { + acceptAllowedCallersArray(item.getLeft(), item.getRight()); + } + } + + + public void acceptAllowedCallersArray(String allowedCallerClaimId, List allowList) { + AllowedCallersClaimsValidator validator = new AllowedCallersClaimsValidator(allowList); + + if (allowedCallerClaimId != null) { + Map claims = createCallerClaims(allowedCallerClaimId); + + if (allowList != null) { + if (allowList.contains(allowedCallerClaimId) || allowList.contains("*")) { + validator.validateClaims(claims); + } else { + validateUnauthorizedAccessException(allowedCallerClaimId, validator, claims); + } + } else { + validateUnauthorizedAccessException(allowedCallerClaimId, validator, claims); + } + } + } + + private static void validateUnauthorizedAccessException( + String allowedCallerClaimId, + AllowedCallersClaimsValidator validator, + Map claims) { + try { + validator.validateClaims(claims); + } catch (RuntimeException exception) { + Assert.assertTrue(exception.getMessage().contains(allowedCallerClaimId)); + } + } + + private Map createCallerClaims(String appId) { + Map callerClaimMap = new HashMap(); + + callerClaimMap.put(AuthenticationConstants.APPID_CLAIM, appId); + callerClaimMap.put(AuthenticationConstants.VERSION_CLAIM, version); + callerClaimMap.put(AuthenticationConstants.AUDIENCE_CLAIM, audienceClaim); + return callerClaimMap; + } +} + diff --git a/samples/80.skills-simple-bot-to-bot/DialogRootBot/pom.xml b/samples/80.skills-simple-bot-to-bot/DialogRootBot/pom.xml index 5bc83ef58..f0ca5da0e 100644 --- a/samples/80.skills-simple-bot-to-bot/DialogRootBot/pom.xml +++ b/samples/80.skills-simple-bot-to-bot/DialogRootBot/pom.xml @@ -84,6 +84,12 @@ 4.6.0-preview9 compile + + com.microsoft.bot + bot-builder + 4.6.0-preview9 + compile + diff --git a/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/Application.java b/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/Application.java index 55cb47636..b7d885fac 100644 --- a/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/Application.java +++ b/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/Application.java @@ -8,6 +8,7 @@ import com.microsoft.bot.builder.ChannelServiceHandler; import com.microsoft.bot.builder.ConversationState; import com.microsoft.bot.builder.MemoryStorage; +import com.microsoft.bot.builder.skills.SkillConversationIdFactory; import com.microsoft.bot.builder.skills.SkillConversationIdFactoryBase; import com.microsoft.bot.builder.skills.SkillHandler; import com.microsoft.bot.connector.authentication.AuthenticationConfiguration; @@ -110,7 +111,7 @@ public SkillHttpClient getSkillHttpClient( @Bean public SkillConversationIdFactoryBase getSkillConversationIdFactoryBase() { - return new SkillConversationIdFactory(); + return new SkillConversationIdFactory(getStorage()); } @Bean public ChannelServiceHandler getChannelServiceHandler( diff --git a/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/SkillConversationIdFactory.java b/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/SkillConversationIdFactory.java deleted file mode 100644 index 1aa0fd42a..000000000 --- a/samples/80.skills-simple-bot-to-bot/DialogRootBot/src/main/java/com/microsoft/bot/sample/simplerootbot/SkillConversationIdFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MT License. - -package com.microsoft.bot.sample.simplerootbot; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import com.microsoft.bot.builder.skills.SkillConversationIdFactoryBase; -import com.microsoft.bot.builder.skills.SkillConversationIdFactoryOptions; -import com.microsoft.bot.builder.skills.SkillConversationReference; - -/** - * A {@link SkillConversationIdFactory} that uses an in memory - * {@link Map{TKey,TValue}} to store and retrieve {@link ConversationReference} - * instances. - */ -public class SkillConversationIdFactory extends SkillConversationIdFactoryBase { - - private final Map _conversationRefs = - new HashMap(); - - @Override - public CompletableFuture createSkillConversationId(SkillConversationIdFactoryOptions options) { - SkillConversationReference skillConversationReference = new SkillConversationReference(); - skillConversationReference.setConversationReference(options.getActivity().getConversationReference()); - skillConversationReference.setOAuthScope(options.getFromBotOAuthScope()); - String key = String.format( - "%s-%s-%s-%s-skillconvo", - options.getFromBotId(), - options.getBotFrameworkSkill().getAppId(), - skillConversationReference.getConversationReference().getConversation().getId(), - skillConversationReference.getConversationReference().getChannelId() - ); - _conversationRefs.put(key, skillConversationReference); - return CompletableFuture.completedFuture(key); - } - - @Override - public CompletableFuture getSkillConversationReference(String skillConversationId) { - SkillConversationReference conversationReference = _conversationRefs.get(skillConversationId); - return CompletableFuture.completedFuture(conversationReference); - } - - @Override - public CompletableFuture deleteConversationReference(String skillConversationId) { - _conversationRefs.remove(skillConversationId); - return CompletableFuture.completedFuture(null); - } -} diff --git a/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/Application.java b/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/Application.java index 52995f61c..3f539d264 100644 --- a/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/Application.java +++ b/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/Application.java @@ -3,13 +3,15 @@ package com.microsoft.bot.sample.echoskillbot; +import java.util.Arrays; + import com.microsoft.bot.builder.Bot; +import com.microsoft.bot.connector.authentication.AllowedCallersClaimsValidator; import com.microsoft.bot.connector.authentication.AuthenticationConfiguration; import com.microsoft.bot.integration.BotFrameworkHttpAdapter; import com.microsoft.bot.integration.Configuration; import com.microsoft.bot.integration.spring.BotController; import com.microsoft.bot.integration.spring.BotDependencyConfiguration; -import com.microsoft.bot.sample.echoskillbot.authentication.AllowedCallersClaimsValidator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -35,6 +37,8 @@ */ public class Application extends BotDependencyConfiguration { + private final String configKey = "AllowedCallers"; + public static void main(String[] args) { SpringApplication.run(Application.class, args); } @@ -57,7 +61,9 @@ public Bot getBot() { @Override public AuthenticationConfiguration getAuthenticationConfiguration(Configuration configuration) { AuthenticationConfiguration authenticationConfiguration = new AuthenticationConfiguration(); - authenticationConfiguration.setClaimsValidator(new AllowedCallersClaimsValidator(configuration)); + authenticationConfiguration.setClaimsValidator( + new AllowedCallersClaimsValidator(Arrays.asList(configuration.getProperties(configKey))) + ); return authenticationConfiguration; } diff --git a/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/authentication/AllowedCallersClaimsValidator.java b/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/authentication/AllowedCallersClaimsValidator.java deleted file mode 100644 index e85d547c7..000000000 --- a/samples/80.skills-simple-bot-to-bot/DialogSkillBot/src/main/java/com/microsoft/bot/sample/echoskillbot/authentication/AllowedCallersClaimsValidator.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MT License. - -package com.microsoft.bot.sample.echoskillbot.authentication; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import com.microsoft.bot.connector.Async; -import com.microsoft.bot.connector.authentication.ClaimsValidator; -import com.microsoft.bot.connector.authentication.JwtTokenValidation; -import com.microsoft.bot.connector.authentication.SkillValidation; -import com.microsoft.bot.integration.Configuration; - -/** - * Sample claims validator that loads an allowed list from configuration if - * presentand checks that requests are coming from allowed parent bots. - */ -public class AllowedCallersClaimsValidator extends ClaimsValidator { - - private final String configKey = "AllowedCallers"; - private final List allowedCallers; - - public AllowedCallersClaimsValidator(Configuration config) { - if (config == null) { - throw new IllegalArgumentException("config cannot be null."); - } - - // AllowedCallers instanceof the setting in the application.properties file - // that consists of the list of parent bot Ds that are allowed to access the - // skill. - // To add a new parent bot, simply edit the AllowedCallers and add - // the parent bot's Microsoft app ID to the list. - // In this sample, we allow all callers if AllowedCallers contains an "*". - String[] appsList = config.getProperties(configKey); - if (appsList == null) { - throw new IllegalStateException(String.format("\"%s\" not found in configuration.", configKey)); - } - - allowedCallers = Arrays.asList(appsList); - } - - @Override - public CompletableFuture validateClaims(Map claims) { - // If _allowedCallers contains an "*", we allow all callers. - if (SkillValidation.isSkillClaim(claims) && !allowedCallers.contains("*")) { - // Check that the appId claim in the skill request instanceof in the list of - // callers configured for this bot. - String appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedCallers.contains(appId)) { - return Async.completeExceptionally( - new RuntimeException( - String.format( - "Received a request from a bot with an app ID of \"%s\". " - + "To enable requests from this caller, add the app ID to your configuration file.", - appId - ) - ) - ); - } - } - - return CompletableFuture.completedFuture(null); - } -} diff --git a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Application.java b/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Application.java index bb2331ac1..dc37fe66a 100644 --- a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Application.java +++ b/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Application.java @@ -8,6 +8,7 @@ import com.microsoft.bot.builder.ChannelServiceHandler; import com.microsoft.bot.builder.ConversationState; import com.microsoft.bot.builder.Storage; +import com.microsoft.bot.builder.skills.SkillConversationIdFactory; import com.microsoft.bot.builder.skills.SkillConversationIdFactoryBase; import com.microsoft.bot.builder.skills.SkillHandler; import com.microsoft.bot.connector.authentication.AuthenticationConfiguration; @@ -18,8 +19,8 @@ import com.microsoft.bot.integration.SkillHttpClient; import com.microsoft.bot.integration.spring.BotController; import com.microsoft.bot.integration.spring.BotDependencyConfiguration; -import com.microsoft.bot.sample.dialogrootbot.Bots.RootBot; import com.microsoft.bot.sample.dialogrootbot.authentication.AllowedSkillsClaimsValidator; +import com.microsoft.bot.sample.dialogrootbot.bots.RootBot; import com.microsoft.bot.sample.dialogrootbot.dialogs.MainDialog; import org.springframework.boot.SpringApplication; diff --git a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Bots/RootBot.java b/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Bots/RootBot.java index 227afb8c4..96b692f0a 100644 --- a/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Bots/RootBot.java +++ b/samples/81.skills-skilldialog/dialog-root-bot/src/main/java/com/microsoft/bot/sample/dialogrootbot/Bots/RootBot.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MT License. -package com.microsoft.bot.sample.dialogrootbot.Bots; +package com.microsoft.bot.sample.dialogrootbot.bots; import java.io.IOException; import java.io.InputStream; diff --git a/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/Application.java b/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/Application.java index 61f160d22..818516bf6 100644 --- a/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/Application.java +++ b/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/Application.java @@ -3,14 +3,16 @@ package com.microsoft.bot.sample.dialogskillbot; +import java.util.Arrays; + import com.microsoft.bot.builder.Bot; import com.microsoft.bot.builder.ConversationState; +import com.microsoft.bot.connector.authentication.AllowedCallersClaimsValidator; import com.microsoft.bot.connector.authentication.AuthenticationConfiguration; import com.microsoft.bot.integration.BotFrameworkHttpAdapter; import com.microsoft.bot.integration.Configuration; import com.microsoft.bot.integration.spring.BotController; import com.microsoft.bot.integration.spring.BotDependencyConfiguration; -import com.microsoft.bot.sample.dialogskillbot.authentication.AllowedCallersClaimsValidator; import com.microsoft.bot.sample.dialogskillbot.bots.SkillBot; import com.microsoft.bot.sample.dialogskillbot.dialogs.ActivityRouterDialog; import com.microsoft.bot.sample.dialogskillbot.dialogs.DialogSkillBotRecognizer; @@ -39,6 +41,8 @@ */ public class Application extends BotDependencyConfiguration { + private final String configKey = "AllowedCallers"; + public static void main(String[] args) { SpringApplication.run(Application.class, args); } @@ -65,7 +69,9 @@ public Bot getBot(Configuration configuration, ConversationState converationStat @Override public AuthenticationConfiguration getAuthenticationConfiguration(Configuration configuration) { AuthenticationConfiguration authenticationConfiguration = new AuthenticationConfiguration(); - authenticationConfiguration.setClaimsValidator(new AllowedCallersClaimsValidator(configuration)); + authenticationConfiguration.setClaimsValidator( + new AllowedCallersClaimsValidator(Arrays.asList(configuration.getProperties(configKey))) + ); return authenticationConfiguration; } diff --git a/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/authentication/AllowedCallersClaimsValidator.java b/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/authentication/AllowedCallersClaimsValidator.java deleted file mode 100644 index c15c81cff..000000000 --- a/samples/81.skills-skilldialog/dialog-skill-bot/src/main/java/com/microsoft/bot/sample/dialogskillbot/authentication/AllowedCallersClaimsValidator.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MT License. - -package com.microsoft.bot.sample.dialogskillbot.authentication; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import com.microsoft.bot.connector.Async; -import com.microsoft.bot.connector.authentication.ClaimsValidator; -import com.microsoft.bot.connector.authentication.JwtTokenValidation; -import com.microsoft.bot.connector.authentication.SkillValidation; -import com.microsoft.bot.integration.Configuration; - -/** - * Sample claims validator that loads an allowed list from configuration if - * presentand checks that requests are coming from allowed parent bots. - */ -public class AllowedCallersClaimsValidator extends ClaimsValidator { - - private final String configKey = "AllowedCallers"; - private final List allowedCallers; - - public AllowedCallersClaimsValidator(Configuration config) { - if (config == null) { - throw new IllegalArgumentException("config cannot be null."); - } - - // AllowedCallers instanceof the setting in the application.properties file - // that consists of the list of parent bot Ds that are allowed to access the - // skill. - // To add a new parent bot, simply edit the AllowedCallers and add - // the parent bot's Microsoft app ID to the list. - // In this sample, we allow all callers if AllowedCallers contains an "*". - String[] appsList = config.getProperties(configKey); - if (appsList == null) { - throw new IllegalStateException(String.format("\"%s\" not found in configuration.", configKey)); - } - - allowedCallers = Arrays.asList(appsList); - } - - @Override - public CompletableFuture validateClaims(Map claims) { - // If _allowedCallers contains an "*", we allow all callers. - if (SkillValidation.isSkillClaim(claims) && !allowedCallers.contains("*")) { - // Check that the appId claim in the skill request instanceof in the list of - // callers configured for this bot. - String appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedCallers.contains(appId)) { - return Async.completeExceptionally( - new RuntimeException( - String.format( - "Received a request from a bot with an app ID of \"%s\". " - + "To enable requests from this caller, add the app ID to your configuration file.", - appId - ) - ) - ); - } - } - - return CompletableFuture.completedFuture(null); - } -}