Skip to content

Commit

Permalink
Merge 18d15f9 into 3348bc1
Browse files Browse the repository at this point in the history
  • Loading branch information
gabog committed Feb 9, 2020
2 parents 3348bc1 + 18d15f9 commit 2f1a6b0
Show file tree
Hide file tree
Showing 60 changed files with 4,307 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;

namespace Microsoft.BotBuilderSamples.EchoSkillBot.Authentication
{
/// <summary>
/// Sample claims validator that loads an allowed list from configuration if present
/// and checks that requests are coming from allowed parent bots.
/// </summary>
public class AllowedCallersClaimsValidator : ClaimsValidator
{
private const string ConfigKey = "AllowedCallers";
private readonly List<string> _allowedCallers;

public AllowedCallersClaimsValidator(IConfiguration config)
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}

// AllowedCallers is the setting in appsettings.json file
// that consists of the list of parent bot ids that are allowed to access the skill
// to add a new parent bot simply go to the AllowedCallers and add
// the parent bot's microsoft app id to the list
var section = config.GetSection(ConfigKey);
var appsList = section.Get<string[]>();
_allowedCallers = appsList != null ? new List<string>(appsList) : null;
}

public override Task ValidateClaimsAsync(IList<Claim> claims)
{
// if _allowedCallers is null we allow all calls
if (_allowedCallers != null && SkillValidation.IsSkillClaim(claims))
{
// Check that the appId claim in the skill request is in the list of skills configured for this bot.
var appId = JwtTokenValidation.GetAppIdFromClaims(claims);
if (!_allowedCallers.Contains(appId))
{
throw new UnauthorizedAccessException($"Received a request from a bot with an app ID of \"{appId}\". To enable requests from this caller, add the app ID to your configuration file.");
}
}

return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;

namespace Microsoft.BotBuilderSamples.EchoSkillBot.Bots
{
public class EchoBot : ActivityHandler
{
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Text.Contains("end") || turnContext.Activity.Text.Contains("stop"))
{
// Send End of conversation at the end.
await turnContext.SendActivityAsync(MessageFactory.Text($"ending conversation from the skill..."), cancellationToken);
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = EndOfConversationCodes.CompletedSuccessfully;
await turnContext.SendActivityAsync(endOfConversation, cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo (dotnet core 3.1) : {turnContext.Activity.Text}"), cancellationToken);
await turnContext.SendActivityAsync(MessageFactory.Text("Say \"end\" or \"stop\" and I'll end the conversation and back to the parent."), cancellationToken);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;

namespace Microsoft.BotBuilderSamples.EchoSkillBot.Controllers
{
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;

public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}

[HttpPost]
public async Task PostAsync()
{
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.EchoSkillBot</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.EchoSkillBot</RootNamespace>
<UserSecretsId>a6aa19d1-4134-48c1-8970-8404e694e003</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:39793/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"EchoSkillBot31": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class SkillAdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public SkillAdapterWithErrorHandler(IConfiguration configuration, ICredentialProvider credentialProvider, AuthenticationConfiguration authConfig, ILogger<BotFrameworkHttpAdapter> logger, ConversationState conversationState = null)
: base(configuration, credentialProvider, authConfig, logger: logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The skill encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception ex)
{
logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
}
}
// Send and EndOfConversation activity to the skill caller with the error to end the conversation
// and let the caller decide what to do.
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = "SkillError";
endOfConversation.Text = exception.Message;
await turnContext.SendActivityAsync(endOfConversation);
// Send a trace activity, which will be displayed in the Bot Framework Emulator
// Note: we return the entire exception in the value property to help the developer, this should not be done in prod.
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.BotBuilderSamples.EchoSkillBot.Authentication;
using Microsoft.BotBuilderSamples.EchoSkillBot.Bots;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();

// Configure credentials
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp => new AuthenticationConfiguration { ClaimsValidator = new AllowedCallersClaimsValidator(sp.GetService<IConfiguration>()) });

// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, SkillAdapterWithErrorHandler>();

// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseDefaultFiles();
app.UseStaticFiles();

//app.UseHttpsRedirection(); Enable this to support https

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"AllowedCallers": []
}

0 comments on commit 2f1a6b0

Please sign in to comment.