diff --git a/libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml b/libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml index 46311a741..ac93ae548 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml @@ -77,6 +77,12 @@ azure-storage-blob ${azure-storage-blob.version} + + + com.azure + azure-identity + ${azure-identity.version} + com.azure 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 58b810be3..d0f4ee3c3 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 @@ -21,7 +21,6 @@ import com.microsoft.bot.builder.teams.TeamsInfo; import com.microsoft.bot.connector.ConnectorClient; import com.microsoft.bot.connector.Conversations; -import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.ChannelAccount; import com.microsoft.bot.schema.ConversationAccount; @@ -40,11 +39,11 @@ */ public abstract class AbstractTeamsConversations implements TeamsConversations { - private MicrosoftAppCredentials mac; + private SpringBotAppCredentials mac; private BotFrameworkAdapter bfa; private ChannelAccount botAccount; - public AbstractTeamsConversations(BotFrameworkAdapter bfa, MicrosoftAppCredentials mac, ChannelAccount botAccount) { + public AbstractTeamsConversations(BotFrameworkAdapter bfa, SpringBotAppCredentials mac, ChannelAccount botAccount) { super(); this.mac = mac; this.bfa = bfa; @@ -145,7 +144,7 @@ protected String getOneToOneConversationId(TeamsUser tu) { try { ConversationParameters cp = new ConversationParameters(); cp.setIsGroup(false); - cp.setTenantId(mac.getChannelAuthTenant()); + cp.setTenantId(mac.getTenantId()); cp.setMembers(Collections.singletonList(new ChannelAccount(tu.getKey()))); return getConversations().createConversation(cp).get().getId(); @@ -159,17 +158,17 @@ public ConversationAccount getConversationAccount(TeamsAddressable address) { if (address instanceof TeamsUser) { String chatForUser = getOneToOneConversationId((TeamsUser) address); ConversationAccount ca = new ConversationAccount(chatForUser); - ca.setTenantId(mac.getChannelAuthTenant()); + ca.setTenantId(mac.getTenantId()); ca.setConversationType("personal"); return ca; } else if (address instanceof TeamsChannel) { ConversationAccount ca = new ConversationAccount(address.getKey()); - ca.setTenantId(mac.getChannelAuthTenant()); + ca.setTenantId(mac.getTenantId()); ca.setConversationType("channel"); return ca; } else if (address instanceof TeamsMultiwayChat) { ConversationAccount ca = new ConversationAccount(address.getKey()); - ca.setTenantId(mac.getChannelAuthTenant()); + ca.setTenantId(mac.getTenantId()); ca.setConversationType("groupChat"); return ca; } else { @@ -226,7 +225,7 @@ private TurnContext getWorkingTurnContext(TeamsAddressable ta) { TurnContext[] holder = new TurnContext[1]; - bfa.continueConversation(mac.getAppId(), createConversationReference(ta), tc -> { + bfa.continueConversation(mac.getClientId(), createConversationReference(ta), tc -> { holder[0] = tc; return CompletableFuture.completedFuture(null); }).get(); 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 new file mode 100644 index 000000000..1626cd312 --- /dev/null +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotAppCredentials.java @@ -0,0 +1,15 @@ +package org.finos.springbot.teams.conversations; + +import com.azure.identity.ClientCertificateCredential; + +public interface SpringBotAppCredentials { + + String getTenantId(); + + String getClientId(); + + ClientCertificateCredential getCredential(); + + String getToken(); + +} \ 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 new file mode 100644 index 000000000..cb2446fe3 --- /dev/null +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/SpringBotMicrosoftAppCredentials.java @@ -0,0 +1,70 @@ +package org.finos.springbot.teams.conversations; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; + +import com.azure.core.credential.TokenRequestContext; +import com.azure.identity.ClientCertificateCredential; +import com.azure.identity.ClientCertificateCredentialBuilder; + +public class SpringBotMicrosoftAppCredentials implements SpringBotAppCredentials { + + private String tenantId = null; + private String clientId = null; + private ClientCertificateCredential credential = null; + + public SpringBotMicrosoftAppCredentials(String tenantId, String clientId, String certificate, + String certificatePassword) { + this.tenantId = tenantId; + this.clientId = clientId; + String pemContent = certificate; + try { + + // Check for file extension and illegal path characters + boolean isFilePath = (certificate != null && (certificate.endsWith(".p12"))); + + if (certificate != null) { + if (!isFilePath) { + byte[] decode = Base64.getDecoder().decode(pemContent); + + java.nio.file.Path tempFile = Files.createTempFile("cert", ".p12"); + Files.write(tempFile, decode); + certificate = tempFile.toAbsolutePath().toString(); + } + + this.credential = new ClientCertificateCredentialBuilder().tenantId(tenantId).clientId(clientId) + .pemCertificate(Files.newInputStream(Paths.get(certificate))) + .clientCertificatePassword(certificatePassword).build(); + } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Failed to create certificate", e); + } + + } + + @Override + public String getTenantId() { + return tenantId; + } + + @Override + public String getClientId() { + return clientId; + } + + @Override + public ClientCertificateCredential getCredential() { + return credential; + } + + @Override + public String getToken() { + return credential.getTokenSync(new TokenRequestContext().addScopes("https://graph.microsoft.com/.default")) + .getToken(); + } + + +} \ 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/StateStorageBasedTeamsConversations.java b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/StateStorageBasedTeamsConversations.java index 1a0562161..3716e3a2b 100644 --- a/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/StateStorageBasedTeamsConversations.java +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/main/java/org/finos/springbot/teams/conversations/StateStorageBasedTeamsConversations.java @@ -21,7 +21,6 @@ import org.finos.springbot.workflow.content.Chat; import com.microsoft.bot.builder.BotFrameworkAdapter; -import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials; import com.microsoft.bot.schema.ChannelAccount; public class StateStorageBasedTeamsConversations extends AbstractTeamsConversations { @@ -34,7 +33,7 @@ public class StateStorageBasedTeamsConversations extends AbstractTeamsConversati protected final TeamsStateStorage tss; - public StateStorageBasedTeamsConversations(BotFrameworkAdapter bfa, MicrosoftAppCredentials mac, + public StateStorageBasedTeamsConversations(BotFrameworkAdapter bfa, SpringBotAppCredentials mac, ChannelAccount botAccount, TeamsStateStorage tss) { super(bfa, mac, botAccount); this.tss = tss; 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 6fa6526ae..1fbbe491d 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 @@ -11,7 +11,6 @@ import org.springframework.context.annotation.Bean; import com.microsoft.bot.builder.BotFrameworkAdapter; -import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials; import com.microsoft.bot.integration.AdapterWithErrorHandler; import com.microsoft.bot.integration.BotFrameworkHttpAdapter; import com.microsoft.bot.schema.ChannelAccount; @@ -19,24 +18,32 @@ public class TeamsConversationsConfig extends BotDependencyConfiguration { @Bean - public MicrosoftAppCredentials microsoftCredentials(@Value("${teams.app.tennantId}") String tennantId) { + public SpringBotAppCredentials microsoftCredentials(@Value("${teams.app.tennantId}") String tennantId) { com.microsoft.bot.integration.Configuration conf = getConfiguration(); - MicrosoftAppCredentials mac = new MicrosoftAppCredentials( - conf.getProperty(MicrosoftAppCredentials.MICROSOFTAPPID), - conf.getProperty(MicrosoftAppCredentials.MICROSOFTAPPPASSWORD), - tennantId); - return mac; + + String clientId = conf.getProperty(com.microsoft.bot.connector.authentication.MicrosoftAppCredentials.MICROSOFTAPPID); + + 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; } + @Bean @ConditionalOnMissingBean public TeamsConversations teamsConversations( BotFrameworkAdapter bfa, - MicrosoftAppCredentials mac, + SpringBotAppCredentials appCredentials, @Value("${teams.bot.id:}") String id, TeamsStateStorage teamsState) { ChannelAccount botAccount = new ChannelAccount(id); - return new StateStorageBasedTeamsConversations(bfa, mac, botAccount, teamsState); + return new StateStorageBasedTeamsConversations(bfa, appCredentials, botAccount, teamsState); } 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 a82d3cac6..bb69065f5 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 @@ -1,8 +1,11 @@ package org.finos.springbot.teams; +import org.finos.springbot.teams.conversations.MockSpringBotMicrosoftAppCredentials; +import org.finos.springbot.teams.conversations.SpringBotAppCredentials; import org.finos.springbot.tests.controller.OurController; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration @@ -18,5 +21,11 @@ public LocalValidatorFactoryBean localValidatorFactoryBean() { public OurController ourController() { return new OurController(); } + + @Bean + @Primary + public SpringBotAppCredentials dummyMicrosoftCredentials() { + return new MockSpringBotMicrosoftAppCredentials(); + } } \ No newline at end of file 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 new file mode 100644 index 000000000..18ca1b3ec --- /dev/null +++ b/libs/teams/teams-chat-workflow-spring-boot-starter/src/test/java/org/finos/springbot/teams/conversations/MockSpringBotMicrosoftAppCredentials.java @@ -0,0 +1,27 @@ +package org.finos.springbot.teams.conversations; + +import com.azure.identity.ClientCertificateCredential; + +public class MockSpringBotMicrosoftAppCredentials implements SpringBotAppCredentials { + + @Override + public String getTenantId() { + return "mock-tenant-id"; + } + + @Override + public String getClientId() { + return "mock-client-id"; + } + + @Override + public ClientCertificateCredential getCredential() { + return null; + } + + @Override + public String getToken() { + return "mock-token"; + } + +} diff --git a/pom.xml b/pom.xml index a911bdb51..4b66e7f21 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,8 @@ 1.17.2 23.0.3 3.0.0 - 1.16.1 + 1.16.1 + 1.18.0 4.5.7 1.21.0 1.16