Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@
<artifactId>azure-storage-blob</artifactId>
<version>${azure-storage-blob.version}</version>
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>${azure-identity.version}</version>
</dependency>

<dependency>
<groupId>com.azure</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
@@ -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();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,39 @@
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;

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);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,5 +21,11 @@ public LocalValidatorFactoryBean localValidatorFactoryBean() {
public OurController ourController() {
return new OurController();
}

@Bean
@Primary
public SpringBotAppCredentials dummyMicrosoftCredentials() {
return new MockSpringBotMicrosoftAppCredentials();
}

}
Original file line number Diff line number Diff line change
@@ -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";
}

}
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
<jsoup.version>1.17.2</jsoup.version>
<graalvm.version>23.0.3</graalvm.version>
<symphony-bdk.version>3.0.0</symphony-bdk.version>
<azure-core-http-netty.version>1.16.1</azure-core-http-netty.version>
<azure-core-http-netty.version>1.16.1</azure-core-http-netty.version>
<azure-identity.version>1.18.0</azure-identity.version>
<corenlp.version>4.5.7</corenlp.version>
<azure-msal4j.version>1.21.0</azure-msal4j.version>
<protonpack.version>1.16</protonpack.version>
Expand Down