diff --git a/Disco.Services/Interop/DiscoServices/ActivationService.cs b/Disco.Services/Interop/DiscoServices/ActivationService.cs
index f96277aa..cfcda7ab 100644
--- a/Disco.Services/Interop/DiscoServices/ActivationService.cs
+++ b/Disco.Services/Interop/DiscoServices/ActivationService.cs
@@ -42,6 +42,24 @@ namespace Disco.Services.Interop.DiscoServices
public Uri GetCallbackUrl()
=> new Uri(DiscoServiceHelpers.ActivationServiceUrl, "/api/callback");
+ public string CalculateCallbackProof(Guid correlationId, string userId, long timestamp)
+ {
+ var deploymentId = Guid.Parse(database.DiscoConfiguration.DeploymentId);
+ var secret = Guid.Parse(database.DiscoConfiguration.DeploymentSecret);
+ using (var hmac = new HMACSHA256(secret.ToByteArray()))
+ {
+ var data = new MemoryStream();
+ data.Write(deploymentId.ToByteArray(), 0, 16);
+ data.Write(correlationId.ToByteArray(), 0, 16);
+ var userIdBytes = Encoding.UTF8.GetBytes(userId);
+ data.Write(BitConverter.GetBytes(userIdBytes.Length), 0, 4);
+ data.Write(userIdBytes, 0, userIdBytes.Length);
+ data.Write(BitConverter.GetBytes(timestamp), 0, 8);
+ var hash = hmac.ComputeHash(data.ToArray());
+ return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_');
+ }
+ }
+
///
/// Begin the activation process
///
diff --git a/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs b/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs
index 6e0ec56f..94054ac4 100644
--- a/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs
+++ b/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs
@@ -43,7 +43,10 @@ namespace Disco.Services.Interop.DiscoServices
connection.Closed += ex =>
{
- SystemLog.LogException("Online Services: Connection Closed", ex);
+ if (ex != null)
+ SystemLog.LogException("Online Services: Connection Closed", ex);
+ else
+ SystemLog.LogInformation("Online Services: Connection Closed");
return Task.CompletedTask;
};
connection.Reconnected += connectionId =>
@@ -53,7 +56,11 @@ namespace Disco.Services.Interop.DiscoServices
};
connection.Reconnecting += ex =>
{
- SystemLog.LogInformation("Online Services: Connection Reconnecting");
+ if (ex != null)
+ SystemLog.LogException("Online Services: Connection Reconnecting", ex);
+ else
+ SystemLog.LogInformation("Online Services: Connection Reconnecting");
+
return Task.CompletedTask;
};
}
diff --git a/Disco.Web/Areas/API/Controllers/ActivationController.cs b/Disco.Web/Areas/API/Controllers/ActivationController.cs
index aa1d3ea4..e94106c9 100644
--- a/Disco.Web/Areas/API/Controllers/ActivationController.cs
+++ b/Disco.Web/Areas/API/Controllers/ActivationController.cs
@@ -12,10 +12,22 @@ namespace Disco.Web.Areas.API.Controllers
[DiscoAuthorize(Claims.DiscoAdminAccount)]
public partial class ActivationController : AuthorizedDatabaseController
{
- [HttpPost]
- public virtual ActionResult TestCallback(CallbackModel model)
+ [HttpGet]
+ public virtual async Task Begin(CallbackModel model)
{
- return this.PrecompiledPartialView(model);
+ // validate timestamp
+ var thresholdStart = DateTimeOffset.UtcNow.AddSeconds(-20).ToUnixTimeMilliseconds();
+ var thresholdEnd = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ if (model.Timestamp < thresholdStart || model.Timestamp > thresholdEnd)
+ return new HttpStatusCodeResult(400, "Invalid timestamp");
+
+ // validate proof
+ var service = new ActivationService(Database);
+ var expectedProof = service.CalculateCallbackProof(model.CorrelationId, model.UserId, model.Timestamp);
+ if (model.Proof != expectedProof)
+ return new HttpStatusCodeResult(400, "Invalid proof");
+
+ return await Begin();
}
[HttpPost, ValidateAntiForgeryToken]
@@ -33,7 +45,7 @@ namespace Disco.Web.Areas.API.Controllers
RedirectUrl = challengeModel.RedirectUrl
};
- return View(model);
+ return View(MVC.API.Activation.Views.Begin, model);
}
[HttpGet]
diff --git a/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs b/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs
index 1d5d5105..9fa7c2f7 100644
--- a/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs
+++ b/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs
@@ -5,9 +5,12 @@ namespace Disco.Web.Areas.API.Models.Activation
{
public class CallbackModel
{
+ public string Origin { get; set; }
public Guid DeploymentId { get; set; }
public Guid CorrelationId { get; set; }
[StringLength(50)]
public string UserId { get; set; }
+ public long Timestamp { get; set; }
+ public string Proof { get; set; }
}
}
diff --git a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml b/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml
deleted file mode 100644
index 7512fdb3..00000000
--- a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml
+++ /dev/null
@@ -1,17 +0,0 @@
-@model Disco.Web.Areas.API.Models.Activation.CallbackModel
-@{
- Layout = null;
-}
-
-
-
-
-
-
-
-
diff --git a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs b/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs
deleted file mode 100644
index dcf94d58..00000000
--- a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-#pragma warning disable 1591
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Disco.Web.Areas.API.Views.Activation
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Web;
- using System.Web.Helpers;
- using System.Web.Mvc;
- using System.Web.Mvc.Ajax;
- using System.Web.Mvc.Html;
- using System.Web.Routing;
- using System.Web.Security;
- using System.Web.UI;
- using System.Web.WebPages;
- using Disco;
- using Disco.Models.Repository;
- using Disco.Services;
- using Disco.Services.Authorization;
- using Disco.Services.Web;
- using Disco.Web;
- using Disco.Web.Extensions;
-
- [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
- [System.Web.WebPages.PageVirtualPathAttribute("~/Areas/API/Views/Activation/_ActivateCallback.cshtml")]
- public partial class _ActivateCallback : Disco.Services.Web.WebViewPage
- {
- public _ActivateCallback()
- {
- }
- public override void Execute()
- {
-
- #line 2 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml"
-
- Layout = null;
-
-
- #line default
- #line hidden
-WriteLiteral("\r\n\r\n\r\n\r\n\r\n
-
-
-
-");
-
- }
- }
-}
-#pragma warning restore 1591
diff --git a/Disco.Web/Areas/Config/Controllers/SystemConfigController.cs b/Disco.Web/Areas/Config/Controllers/SystemConfigController.cs
index 925688e6..dae60400 100644
--- a/Disco.Web/Areas/Config/Controllers/SystemConfigController.cs
+++ b/Disco.Web/Areas/Config/Controllers/SystemConfigController.cs
@@ -30,7 +30,9 @@ namespace Disco.Web.Areas.Config.Controllers
DeploymentId = Guid.Parse(Database.DiscoConfiguration.DeploymentId),
CorrelationId = Guid.NewGuid(),
UserId = CurrentUser.UserId,
+ Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
};
+ model.Proof = service.CalculateCallbackProof(model.CorrelationId, model.UserId, model.Timestamp);
return View(model);
}
diff --git a/Disco.Web/Areas/Config/Models/SystemConfig/ActivateModel.cs b/Disco.Web/Areas/Config/Models/SystemConfig/ActivateModel.cs
index 17ad8259..6d64650b 100644
--- a/Disco.Web/Areas/Config/Models/SystemConfig/ActivateModel.cs
+++ b/Disco.Web/Areas/Config/Models/SystemConfig/ActivateModel.cs
@@ -4,9 +4,11 @@ namespace Disco.Web.Areas.Config.Models.SystemConfig
{
public class ActivateModel
{
- public Uri CallbackUrl { get; set; }
public Guid DeploymentId { get; set; }
public Guid CorrelationId { get; set; }
public string UserId { get; set; }
+ public long Timestamp { get; set; }
+ public string Proof { get; set; }
+ public Uri CallbackUrl { get; set; }
}
}
diff --git a/Disco.Web/Areas/Config/Views/SystemConfig/Activate.cshtml b/Disco.Web/Areas/Config/Views/SystemConfig/Activate.cshtml
index 55aab05c..38f513b6 100644
--- a/Disco.Web/Areas/Config/Views/SystemConfig/Activate.cshtml
+++ b/Disco.Web/Areas/Config/Views/SystemConfig/Activate.cshtml
@@ -11,45 +11,23 @@
-
-
-
-@using (Html.BeginForm(MVC.API.Activation.Begin(), FormMethod.Post, new { id = "activationBegin"}))
-{
- @Html.AntiForgeryToken()
-}
-
-
-
diff --git a/Disco.Web/Areas/Config/Views/SystemConfig/Activate.generated.cs b/Disco.Web/Areas/Config/Views/SystemConfig/Activate.generated.cs
index 96404097..58acaacf 100644
--- a/Disco.Web/Areas/Config/Views/SystemConfig/Activate.generated.cs
+++ b/Disco.Web/Areas/Config/Views/SystemConfig/Activate.generated.cs
@@ -68,44 +68,47 @@ WriteLiteral(">\r\n Testing Connectivity to Disco ICT Online Services
\r\n \r\n\r" +
-"\n\r\n\r\n\r\n\r\n\r\n");
+WriteLiteral(" />\r\n (new Uri(Request.Url, Url.Action(MVC.API.Activation.Begin()))
#line default
#line hidden
-
- #line 26 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
-Write(Html.AntiForgeryToken());
-
-
- #line default
- #line hidden
-
- #line 26 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
-
-}
-
-
- #line default
- #line hidden
-WriteLiteral("\r\n(Url.Action(MVC.Config.SystemConfig.Index())
-
- #line default
- #line hidden
-, 1161), false)
+, 1017), false)
);
-WriteLiteral(" class=\"hidden\"");
-
-WriteLiteral(@">
+WriteLiteral(@" />
+
diff --git a/Disco.Web/Disco.Web.csproj b/Disco.Web/Disco.Web.csproj
index 5f65cd17..83fa1731 100644
--- a/Disco.Web/Disco.Web.csproj
+++ b/Disco.Web/Disco.Web.csproj
@@ -442,11 +442,6 @@
True
True
-
- True
- True
- _ActivateCallback.cshtml
-
Create.cshtml
True
@@ -1518,10 +1513,6 @@
RazorGenerator
Begin.generated.cs
-
- RazorGenerator
- _ActivateCallback.generated.cs
-
RazorGenerator
Create.generated.cs
diff --git a/Disco.Web/Extensions/T4MVC/API.ActivationController.generated.cs b/Disco.Web/Extensions/T4MVC/API.ActivationController.generated.cs
index fa2a9e19..688362a9 100644
--- a/Disco.Web/Extensions/T4MVC/API.ActivationController.generated.cs
+++ b/Disco.Web/Extensions/T4MVC/API.ActivationController.generated.cs
@@ -59,12 +59,6 @@ namespace Disco.Web.Areas.API.Controllers
return RedirectToActionPermanent(taskResult.Result);
}
- [NonAction]
- [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
- public virtual System.Web.Mvc.ActionResult TestCallback()
- {
- return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.TestCallback);
- }
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Threading.Tasks.Task Complete()
@@ -88,7 +82,6 @@ namespace Disco.Web.Areas.API.Controllers
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionNamesClass
{
- public readonly string TestCallback = "TestCallback";
public readonly string Begin = "Begin";
public readonly string Complete = "Complete";
}
@@ -96,17 +89,16 @@ namespace Disco.Web.Areas.API.Controllers
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionNameConstants
{
- public const string TestCallback = "TestCallback";
public const string Begin = "Begin";
public const string Complete = "Complete";
}
- static readonly ActionParamsClass_TestCallback s_params_TestCallback = new ActionParamsClass_TestCallback();
+ static readonly ActionParamsClass_Begin s_params_Begin = new ActionParamsClass_Begin();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
- public ActionParamsClass_TestCallback TestCallbackParams { get { return s_params_TestCallback; } }
+ public ActionParamsClass_Begin BeginParams { get { return s_params_Begin; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
- public class ActionParamsClass_TestCallback
+ public class ActionParamsClass_Begin
{
public readonly string model = "model";
}
@@ -131,10 +123,8 @@ namespace Disco.Web.Areas.API.Controllers
public _ViewNamesClass ViewNames { get { return s_ViewNames; } }
public class _ViewNamesClass
{
- public readonly string _ActivateCallback = "_ActivateCallback";
public readonly string Begin = "Begin";
}
- public readonly string _ActivateCallback = "~/Areas/API/Views/Activation/_ActivateCallback.cshtml";
public readonly string Begin = "~/Areas/API/Views/Activation/Begin.cshtml";
}
}
@@ -145,15 +135,15 @@ namespace Disco.Web.Areas.API.Controllers
public T4MVC_ActivationController() : base(Dummy.Instance) { }
[NonAction]
- partial void TestCallbackOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, Disco.Web.Areas.API.Models.Activation.CallbackModel model);
+ partial void BeginOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, Disco.Web.Areas.API.Models.Activation.CallbackModel model);
[NonAction]
- public override System.Web.Mvc.ActionResult TestCallback(Disco.Web.Areas.API.Models.Activation.CallbackModel model)
+ public override System.Threading.Tasks.Task Begin(Disco.Web.Areas.API.Models.Activation.CallbackModel model)
{
- var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.TestCallback);
+ var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Begin);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "model", model);
- TestCallbackOverride(callInfo, model);
- return callInfo;
+ BeginOverride(callInfo, model);
+ return System.Threading.Tasks.Task.FromResult(callInfo as System.Web.Mvc.ActionResult);
}
[NonAction]