Skip to content

Instantly share code, notes, and snippets.

@laurentkempe
Last active May 17, 2016 20:17
Show Gist options
  • Select an option

  • Save laurentkempe/38b53ab6c53b15a9630580b6115d2067 to your computer and use it in GitHub Desktop.

Select an option

Save laurentkempe/38b53ab6c53b15a9630580b6115d2067 to your computer and use it in GitHub Desktop.
ASP.NET Core RC2, Docker and HipChat Connect add-on
FROM microsoft/dotnet
# Set environment variables
ENV ASPNETCORE_URLS="http://*:5000"
ENV ASPNETCORE_ENVIRONMENT="Development"
# Copy files to app directory
COPY . /app
# Set working directory
WORKDIR /app
# Restore NuGet packages
RUN ["dotnet", "restore"]
# Open up port
EXPOSE 5000
# Run the app
ENTRYPOINT ["dotnet", "run"]
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Nubot.Plugins.Samples.HipChatConnect.Models;
namespace HipChatConnect.Controllers
{
[Route("/hipchat")]
public class HipChatConnectController : Controller
{
private readonly IOptions<AppSettings> _settings;
private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>();
public HipChatConnectController(IOptions<AppSettings> settings)
{
_settings = settings;
}
[HttpGet("atlassian-connect.json")]
public async Task<string> Get()
{
var baseUri = _settings.Value?.NGrokUrl ?? "http://localhost:52060/";
return await Task.FromResult(GetCapabilitiesDescriptor(baseUri));
}
[HttpPost("installable")]
public async Task<HttpStatusCode> Installable([FromBody]InstallationData installationData)
{
Cache.TryAdd(installationData.oauthId, installationData);
var capabilitiesRoot = await GetCapabilitiesRoot(installationData);
var accessToken = await GetAccessToken(installationData, capabilitiesRoot);
return Cache.TryAdd("accessToken", accessToken) ? HttpStatusCode.OK : HttpStatusCode.NotFound;
}
[HttpGet("uninstalled")]
public async Task<IActionResult> UnInstalled([FromQuery(Name = "redirect_url")]string redirectUrl,
[FromQuery(Name = "installable_url")]string installableUrl)
{
var client = new HttpClient();
var httpResponse = await client.GetAsync(installableUrl);
httpResponse.EnsureSuccessStatusCode();
var jObject = await httpResponse.Content.ReadAsAsync<JObject>();
object anobject ;
Cache.TryRemove((string)jObject["oauthId"], out anobject);
return await Task.FromResult(Redirect(redirectUrl));
}
[HttpGet("glance")]
public string GetGlance([FromQuery(Name = "signed_request")]string signedRequest)
{
if (ValidateToken(signedRequest))
{
return BuildInitialGlance();
}
return "";
}
private static string GetCapabilitiesDescriptor(string baseUri)
{
var capabilitiesDescriptor = new
{
name = "Nubot",
description = "An add-on to talk to Nubot.",
key = "nubot-addon",
links = new
{
self = $"{baseUri}/hipchat/atlassian-connect.json",
homepage = $"{baseUri}/hipchat/atlassian-connect.json"
},
vendor = new
{
name = "Laurent Kempe",
url = "http://laurentkempe.com"
},
capabilities = new
{
hipchatApiConsumer = new
{
scopes = new[]
{
"send_notification",
"view_room"
}
},
installable = new
{
callbackUrl = $"{baseUri}/hipchat/installable",
uninstalledUrl = $"{baseUri}/hipchat/uninstalled"
},
glance = new[]
{
new
{
name = new
{
value = "Hello TC"
},
queryUrl = $"{baseUri}/hipchat/glance",
key = "nubot.glance",
target = "nubot.sidebar",
icon = new Icon
{
url = $"{baseUri}/nubot/TC.png",
url2 = $"{baseUri}/nubot/TC2.png"
}
}
}
}
};
return JsonConvert.SerializeObject(capabilitiesDescriptor);
}
private async Task<CapabilitiesRoot> GetCapabilitiesRoot(InstallationData installationData)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(new Uri(installationData.capabilitiesUrl));
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<CapabilitiesRoot>();
}
private async Task<AccessToken> GetAccessToken(InstallationData installationData, CapabilitiesRoot capabilitiesRoot)
{
var client = new HttpClient();
var dataContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("scope", "send_notification")
});
var credentials = Encoding.ASCII.GetBytes($"{installationData.oauthId}:{installationData.oauthSecret}");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(credentials));
var tokenResponse =
await client.PostAsync(new Uri(capabilitiesRoot.capabilities.oauth2Provider.tokenUrl), dataContent);
return await tokenResponse.Content.ReadAsAsync<AccessToken>();
}
private bool ValidateToken(string jwt)
{
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
var readToken = jwtSecurityTokenHandler.ReadToken(jwt);
object installationDataObject;
if (!Cache.TryGetValue(readToken.Issuer, out installationDataObject))
{
return false;
}
var installationData = (InstallationData)installationDataObject;
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = installationData.oauthId,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(installationData.oauthSecret)),
ValidateAudience = false,
ValidateLifetime = true
};
try
{
SecurityToken token;
var validatedToken = jwtSecurityTokenHandler.ValidateToken(jwt, validationParameters, out token);
return validatedToken != null;
}
catch (Exception)
{
return false;
}
}
private static string BuildInitialGlance()
{
var response = new
{
label = new
{
type = "html",
value = "<b>4</b> Builds"
},
status = new
{
type = "lozenge",
value = new
{
label = "GOOD",
type = "success"
}
},
metadata = new
{
customData = new { customAttr = "customValue" }
}
};
return JsonConvert.SerializeObject(response);
}
}
}
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace HipChatConnect
{
public class Program
{
public static void Main(string[] args)
{
// Get environment variables
var config = new ConfigurationBuilder()
.AddEnvironmentVariables("")
.Build();
var url = config["ASPNETCORE_URLS"] ?? "http://*:5000";
var env = config["ASPNETCORE_ENVIRONMENT"] ?? "Development";
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls(url)
.UseEnvironment(env)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace HipChatConnect
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(settings => settings.NGrokUrl = Configuration["NGROK_URL"]);
// Add framework services.
services.AddCors();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseCors(builder =>
{
builder.WithOrigins("*")
.WithMethods("GET")
.WithHeaders("accept", "content-type", "origin");
});
app.UseMvc();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment