diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/TeamsWorkflowConfig.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/TeamsWorkflowConfig.java index 446a49dea..23321d232 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/TeamsWorkflowConfig.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/TeamsWorkflowConfig.java @@ -61,6 +61,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.microsoft.bot.builder.TurnContext; import com.microsoft.bot.builder.teams.TeamsActivityHandler; +import com.microsoft.bot.connector.teams.TeamsConnectorClient; import com.microsoft.bot.integration.BotFrameworkHttpAdapter; import com.microsoft.bot.schema.ChannelAccount; @@ -230,8 +231,9 @@ public TeamsActivityHandler teamsActivityHandler( FormValidationProcessor fvp, TeamsConversations tc, TeamsStateStorage teamsStateStorage, - TeamsFormConverter fc) { - return new FileActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp); + TeamsFormConverter fc, + TeamsConnectorClient teamsConnectorClient) { + return new FileActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp, teamsConnectorClient); } @Bean diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/AbstractTeamsConversations.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/AbstractTeamsConversations.java index d0f4ee3c3..b08c317af 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/AbstractTeamsConversations.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/AbstractTeamsConversations.java @@ -219,9 +219,9 @@ private TurnContext getWorkingTurnContext(TeamsAddressable ta) { try { TurnContext out = CurrentTurnContext.CURRENT_CONTEXT.get(); - if (out != null) { - return out; - } +// if (out != null) { +// return out; +// } TurnContext[] holder = new TurnContext[1]; diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotAppCredentials.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotAppCredentials.java index 1626cd312..6965d0f5f 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotAppCredentials.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotAppCredentials.java @@ -1,6 +1,7 @@ package org.finos.springbot.teams.conversations; import com.azure.identity.ClientCertificateCredential; +import com.microsoft.bot.connector.authentication.CertificateAppCredentials; public interface SpringBotAppCredentials { @@ -12,4 +13,6 @@ public interface SpringBotAppCredentials { String getToken(); + CertificateAppCredentials getAppCredentials(); + } \ No newline at end of file diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotMicrosoftAppCredentials.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotMicrosoftAppCredentials.java index cb2446fe3..18a313630 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotMicrosoftAppCredentials.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotMicrosoftAppCredentials.java @@ -3,18 +3,26 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Base64; import com.azure.core.credential.TokenRequestContext; import com.azure.identity.ClientCertificateCredential; import com.azure.identity.ClientCertificateCredentialBuilder; +import com.microsoft.bot.connector.authentication.CertificateAppCredentials; +import com.microsoft.bot.connector.authentication.CertificateAppCredentialsOptions; public class SpringBotMicrosoftAppCredentials implements SpringBotAppCredentials { private String tenantId = null; private String clientId = null; private ClientCertificateCredential credential = null; - + private CertificateAppCredentials appCredentials = null; + public SpringBotMicrosoftAppCredentials(String tenantId, String clientId, String certificate, String certificatePassword) { this.tenantId = tenantId; @@ -37,14 +45,21 @@ public SpringBotMicrosoftAppCredentials(String tenantId, String clientId, String this.credential = new ClientCertificateCredentialBuilder().tenantId(tenantId).clientId(clientId) .pemCertificate(Files.newInputStream(Paths.get(certificate))) .clientCertificatePassword(certificatePassword).build(); + + CertificateAppCredentialsOptions out = new CertificateAppCredentialsOptions(clientId, + Files.newInputStream(Paths.get(certificate)), certificatePassword); + + appCredentials = new CertificateAppCredentials(out); + } - } catch (IOException e) { + } catch (IOException | UnrecoverableKeyException | CertificateException | NoSuchAlgorithmException + | KeyStoreException | NoSuchProviderException e) { e.printStackTrace(); throw new RuntimeException("Failed to create certificate", e); } } - + @Override public String getTenantId() { return tenantId; @@ -61,10 +76,22 @@ public ClientCertificateCredential getCredential() { } @Override - public String getToken() { - return credential.getTokenSync(new TokenRequestContext().addScopes("https://graph.microsoft.com/.default")) - .getToken(); + public CertificateAppCredentials getAppCredentials() { + return appCredentials; } - + + + @Override + public String getToken() { + + String token = credential.getTokenSync( + new TokenRequestContext().addScopes("https://api.botframework.com/.default") + ).getToken(); + + return token; + + } + + } \ No newline at end of file diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/TeamsConversationsConfig.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/TeamsConversationsConfig.java index 48827405d..bf415e5e7 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/TeamsConversationsConfig.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/TeamsConversationsConfig.java @@ -9,16 +9,23 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import com.microsoft.bot.builder.BotFrameworkAdapter; +import com.microsoft.bot.connector.authentication.AuthenticationConfiguration; +import com.microsoft.bot.connector.authentication.CertificateAppCredentials; +import com.microsoft.bot.connector.authentication.ChannelProvider; +import com.microsoft.bot.connector.rest.RestTeamsConnectorClient; +import com.microsoft.bot.connector.teams.TeamsConnectorClient; import com.microsoft.bot.integration.AdapterWithErrorHandler; import com.microsoft.bot.integration.BotFrameworkHttpAdapter; +import com.microsoft.bot.integration.Configuration; import com.microsoft.bot.schema.ChannelAccount; public class TeamsConversationsConfig extends BotDependencyConfiguration { @Bean - public SpringBotAppCredentials microsoftCredentials(@Value("${teams.app.tennantId}") String tennantId) { + public SpringBotAppCredentials springBotAppCredentials(@Value("${teams.app.tennantId}") String tennantId) { com.microsoft.bot.integration.Configuration conf = getConfiguration(); String clientId = conf @@ -27,15 +34,42 @@ public SpringBotAppCredentials microsoftCredentials(@Value("${teams.app.tennantI SpringBotAppCredentials out = new SpringBotMicrosoftAppCredentials(tennantId, clientId, conf.getProperty("MicrosoftAppIdPemCertificate"), conf.getProperty("MicrosoftAppIdPemCertificatePassword")); - - // MicrosoftAppCredentials mac = new MicrosoftAppCredentials( - // conf.getProperty(MicrosoftAppCredentials.MICROSOFTAPPID), - // conf.getProperty(MicrosoftAppCredentials.MICROSOFTAPPPASSWORD), - // tennantId); - return out; } - + + @Primary + @Bean + public CertificateAppCredentials certificateCredentials(SpringBotAppCredentials credentials) { + return credentials.getAppCredentials(); + } + + @Bean + public TeamsConnectorClient restTeamsConnectorClient(CertificateAppCredentials credentials) { + return new RestTeamsConnectorClient("https://smba.trafficmanager.net/uk/", credentials); + } + + @Primary + @Bean + public BotFrameworkAdapter botFrameworkAdapter( + CertificateAppCredentials withCredentials, + AuthenticationConfiguration withAuthConfig, + ChannelProvider withChannelProvider) { + + Configuration conf = getConfiguration(); + + String clientId = conf.getProperty("MicrosoftAppId"); + withCredentials.setAppId(clientId); + + BotFrameworkAdapter adapter = new BotFrameworkAdapter(withCredentials, + withAuthConfig, + withChannelProvider, + null, + null); + + return adapter; + } + + @Bean @ConditionalOnMissingBean public TeamsConversations teamsConversations( diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/FileActivityHandler.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/FileActivityHandler.java index 0e21c0bec..39c3da587 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/FileActivityHandler.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/FileActivityHandler.java @@ -24,6 +24,7 @@ import com.microsoft.bot.builder.MessageFactory; import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.connector.teams.TeamsConnectorClient; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.Attachment; import com.microsoft.bot.schema.ResultPair; @@ -38,8 +39,9 @@ public class FileActivityHandler extends MessageActivityHandler { public FileActivityHandler(List messageConsumers, TeamsConversations teamsConversations, TeamsStateStorage teamsStateStorage, TeamsHTMLParser parser, FormConverter formConverter, - FormValidationProcessor validationProcessor) { - super(messageConsumers, teamsConversations, teamsStateStorage, parser, formConverter, validationProcessor); + FormValidationProcessor validationProcessor, TeamsConnectorClient teamsConnectorClient) { + super(messageConsumers, teamsConversations, teamsStateStorage, parser, formConverter, validationProcessor, + teamsConnectorClient); } @Override diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/MessageActivityHandler.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/MessageActivityHandler.java index dc9c6e454..ee589555d 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/MessageActivityHandler.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/messages/MessageActivityHandler.java @@ -26,8 +26,10 @@ import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; +import com.microsoft.bot.builder.BotFrameworkAdapter; import com.microsoft.bot.builder.TurnContext; import com.microsoft.bot.builder.teams.TeamsActivityHandler; +import com.microsoft.bot.connector.teams.TeamsConnectorClient; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.Attachment; @@ -41,6 +43,7 @@ public class MessageActivityHandler extends TeamsActivityHandler { TeamsStateStorage teamsStateStorage; FormConverter formConverter; FormValidationProcessor validationProcessor; + TeamsConnectorClient teamsConnectorClient; public MessageActivityHandler( List messageConsumers, @@ -48,7 +51,8 @@ public MessageActivityHandler( TeamsStateStorage teamsStateStorage, TeamsHTMLParser parser, FormConverter formConverter, - FormValidationProcessor validationProcessor) { + FormValidationProcessor validationProcessor, + TeamsConnectorClient teamsConnectorClient) { super(); this.messageConsumers = messageConsumers; this.teamsConversations = teamsConversations; @@ -56,6 +60,7 @@ public MessageActivityHandler( this.messageParser = parser; this.formConverter = formConverter; this.validationProcessor = validationProcessor; + this.teamsConnectorClient = teamsConnectorClient; } @Override @@ -69,6 +74,11 @@ public void handleActivity(TurnContext turnContext) { Activity a = turnContext.getActivity(); try { + + turnContext.getTurnState().remove(BotFrameworkAdapter.TEAMSCONNECTOR_CLIENT_KEY); + turnContext.getTurnState().add(BotFrameworkAdapter.TEAMSCONNECTOR_CLIENT_KEY, teamsConnectorClient); + + CurrentTurnContext.CURRENT_CONTEXT.set(turnContext); Action action = (a.getValue() != null) ? processForm(turnContext, a) : processMessage(turnContext, a); diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/MockTeamsConfiguration.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/MockTeamsConfiguration.java index 5d2d7650d..0e3b7ab1c 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/MockTeamsConfiguration.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/MockTeamsConfiguration.java @@ -8,6 +8,8 @@ import org.springframework.context.annotation.Primary; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import com.microsoft.bot.connector.authentication.CertificateAppCredentials; + @Configuration public class MockTeamsConfiguration { @@ -27,4 +29,9 @@ public SpringBotAppCredentials dummyMicrosoftCredentials() { return new MockSpringBotMicrosoftAppCredentials(); } + @Bean + public CertificateAppCredentials certificateAppCredentials(SpringBotAppCredentials appCredentials) { + return appCredentials.getAppCredentials(); + } + } \ No newline at end of file diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/controller/TeamsHandlerMappingTest.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/controller/TeamsHandlerMappingTest.java index e320d6107..45336cf21 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/controller/TeamsHandlerMappingTest.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/controller/TeamsHandlerMappingTest.java @@ -46,6 +46,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.builder.TurnContextStateCollection; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.ActivityTypes; import com.microsoft.bot.schema.Attachment; @@ -219,7 +220,10 @@ public static CompletableFuture failed(Throwable error) { private void mockTurnContext(String s, Map formData, boolean isAttachement) { tc = Mockito.mock(TurnContext.class); CurrentTurnContext.CURRENT_CONTEXT.set(tc); - + + TurnContextStateCollection tcsc = Mockito.mock(TurnContextStateCollection.class); + Mockito.when(tc.getTurnState()).thenReturn(tcsc); + msg = ArgumentCaptor.forClass(Activity.class); Mockito.when(tc.sendActivity(msg.capture())).thenReturn(CompletableFuture.completedFuture(null)); diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/conversations/MockSpringBotMicrosoftAppCredentials.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/conversations/MockSpringBotMicrosoftAppCredentials.java index 18ca1b3ec..2f817c5e4 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/conversations/MockSpringBotMicrosoftAppCredentials.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/conversations/MockSpringBotMicrosoftAppCredentials.java @@ -1,6 +1,9 @@ package org.finos.springbot.teams.conversations; +import org.mockito.Mockito; + import com.azure.identity.ClientCertificateCredential; +import com.microsoft.bot.connector.authentication.CertificateAppCredentials; public class MockSpringBotMicrosoftAppCredentials implements SpringBotAppCredentials { @@ -24,4 +27,9 @@ public String getToken() { return "mock-token"; } + @Override + public CertificateAppCredentials getAppCredentials() { + return Mockito.mock(CertificateAppCredentials.class); + } + }