aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Web
diff options
context:
space:
mode:
authorAvi Levkovich <avi@twine-s.com>2019-02-21 17:34:53 +0200
committerAvi Levkovich <avi@twine-s.com>2019-02-21 17:34:53 +0200
commitd7c8a8e9a6320ade6098e0d8e182c7ada4e30a97 (patch)
tree4f261cdd6c2f5a3336e09e775576fbd6bf353dd1 /Software/Visual_Studio/Tango.Web
parent1b6e37351cde492bc27e48d4c53f6a69eea91052 (diff)
parenta5fde6c76b7c98c482d5fc069c371da5480c0bdf (diff)
downloadTango-d7c8a8e9a6320ade6098e0d8e182c7ada4e30a97.tar.gz
Tango-d7c8a8e9a6320ade6098e0d8e182c7ada4e30a97.zip
Merge branch 'master' of https://twinetfs.visualstudio.com/_git/Tango
Diffstat (limited to 'Software/Visual_Studio/Tango.Web')
-rw-r--r--Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs6
-rw-r--r--Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs151
-rw-r--r--Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs2
-rw-r--r--Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs (renamed from Software/Visual_Studio/Tango.Web/Controllers/JsonController.cs)61
-rw-r--r--Software/Visual_Studio/Tango.Web/Tango.Web.csproj18
-rw-r--r--Software/Visual_Studio/Tango.Web/TangoWebClient.cs82
-rw-r--r--Software/Visual_Studio/Tango.Web/app.config6
-rw-r--r--Software/Visual_Studio/Tango.Web/packages.config3
8 files changed, 264 insertions, 65 deletions
diff --git a/Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs b/Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs
index 890d69d53..5829bfca3 100644
--- a/Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs
+++ b/Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs
@@ -48,8 +48,8 @@ namespace Tango.Web.Authentication
Value = tokenObject,
WebToken = new WebToken()
{
- AccessToken = token,
- Expiration = DateTime.UtcNow.Add(ExpirationTime)
+ //AccessToken = token,
+ //Expiration = DateTime.UtcNow.Add(ExpirationTime)
},
};
@@ -73,7 +73,7 @@ namespace Tango.Web.Authentication
if (DateTime.UtcNow > _tokens[token].WebToken.Expiration)
{
_tokens.Remove(token);
- throw new SessionExpiredException("Session Expired.");
+ throw new TokenExpiredException("Session Expired.");
}
return _tokens[token].Value;
diff --git a/Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs b/Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs
index 71ec6eb0b..14fc49942 100644
--- a/Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs
+++ b/Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs
@@ -1,6 +1,12 @@
-using System;
+using JWT;
+using JWT.Algorithms;
+using JWT.Builder;
+using JWT.Serializers;
+using Newtonsoft.Json;
+using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
@@ -8,7 +14,146 @@ namespace Tango.Web.Authentication
{
public class WebToken
{
- public DateTime Expiration { get; set; }
- public String AccessToken { get; set; }
+ public DateTime Issued { get; protected set; }
+ public DateTime? Expiration { get; protected set; }
+ public String AccessToken { get; protected set; }
+
+ public WebToken()
+ {
+
+ }
+
+ public static WebToken CreateNew(String secret, DateTime? expiration = null)
+ {
+ DateTime issued = DateTime.UtcNow;
+
+ var builder = new JwtBuilder()
+ .WithAlgorithm(new HMACSHA256Algorithm())
+ .WithSecret(secret)
+ .IssuedAt(issued);
+
+ if (expiration != null)
+ {
+ builder = builder.ExpirationTime(expiration.Value);
+ }
+
+ builder = builder.AddClaim("object", null);
+
+ return new WebToken()
+ {
+ AccessToken = builder.Build(),
+ Expiration = expiration,
+ Issued = issued,
+ };
+ }
+
+ public static void Validate(String secret, String token)
+ {
+ var json = new JwtBuilder()
+ .WithSecret(secret)
+ .MustVerifySignature()
+ .Decode(token);
+ }
+
+ public void Validate(String secret)
+ {
+ var json = new JwtBuilder()
+ .WithSecret(secret)
+ .MustVerifySignature()
+ .Decode(AccessToken);
+ }
+
+ public static WebToken FromToken(String token)
+ {
+ WebToken webToken = new WebToken();
+
+ var payload = new JwtBuilder()
+ .WithValidator(null)
+ .Decode<IDictionary<string, object>>(token);
+
+ webToken.AccessToken = token;
+
+ if (payload.ContainsKey("exp"))
+ {
+ long exp = long.Parse(payload["exp"].ToString());
+ webToken.Expiration = ConvertEpochToDateTime(exp);
+ }
+
+ if (payload.ContainsKey("iat"))
+ {
+ long iat = long.Parse(payload["iat"].ToString());
+ webToken.Issued = ConvertEpochToDateTime(iat);
+ }
+
+ return webToken;
+ }
+
+ protected static DateTime ConvertEpochToDateTime(long seconds)
+ {
+ var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ return epoch.AddSeconds(seconds);
+ }
+ }
+
+ public class WebToken<T> : WebToken where T : class
+ {
+ public T Object { get; protected set; }
+
+ private WebToken()
+ {
+
+ }
+
+ public static WebToken<T> CreateNew(String secret, T obj = null, DateTime? expiration = null)
+ {
+ DateTime issued = DateTime.UtcNow;
+
+ var builder = new JwtBuilder()
+ .WithAlgorithm(new HMACSHA256Algorithm())
+ .WithSecret(secret)
+ .IssuedAt(issued);
+
+ if (expiration != null)
+ {
+ builder = builder.ExpirationTime(expiration.Value);
+ }
+
+ builder = builder.AddClaim("object", obj);
+
+ return new WebToken<T>()
+ {
+ AccessToken = builder.Build(),
+ Expiration = expiration,
+ Issued = issued,
+ Object = obj,
+ };
+ }
+
+ public static new WebToken<T> FromToken(String token)
+ {
+ WebToken<T> webToken = new WebToken<T>();
+
+ var payload = new JwtBuilder()
+ .WithValidator(null)
+ .Decode<IDictionary<string, object>>(token);
+
+ webToken.AccessToken = token;
+
+ if (payload.ContainsKey("exp"))
+ {
+ long exp = long.Parse(payload["exp"].ToString());
+ webToken.Expiration = ConvertEpochToDateTime(exp);
+ }
+
+ if (payload.ContainsKey("iat"))
+ {
+ long iat = long.Parse(payload["iat"].ToString());
+ webToken.Issued = ConvertEpochToDateTime(iat);
+ }
+
+ webToken.Object = JsonConvert.DeserializeObject<T>(payload["object"].ToString());
+
+ return webToken;
+ }
}
}
diff --git a/Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs b/Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs
index 17ac6636f..190a47cc2 100644
--- a/Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs
+++ b/Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs
@@ -9,6 +9,6 @@ namespace Tango.Web.Authentication
{
public class WebTokenResponse : WebResponseMessage
{
- public WebToken WebToken { get; set; }
+ public String AccessToken { get; set; }
}
}
diff --git a/Software/Visual_Studio/Tango.Web/Controllers/JsonController.cs b/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs
index 1fae9cccc..854d1cf96 100644
--- a/Software/Visual_Studio/Tango.Web/Controllers/JsonController.cs
+++ b/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs
@@ -10,14 +10,16 @@ using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using Tango.Logging;
+using Tango.Transport.Web;
+using Tango.Web.Authentication;
namespace Tango.Web.Controllers
{
- public class JsonController : ApiController
+ public class TangoController : ApiController
{
protected LogManager LogManager { get; private set; }
- public JsonController()
+ public TangoController()
{
LogManager = LogManager.Default;
}
@@ -43,10 +45,12 @@ namespace Tango.Web.Controllers
{
request = context.Request.Content.ReadAsStringAsync().Result;
}
- catch {}
+ catch { }
LogManager.Log($"Request Received on {controllerName + "/" + actionName}: \n{request}");
+ OnRequestArrived(context.Request);
+
var result = await base.ExecuteAsync(context, cancellationToken);
return result;
}
@@ -60,16 +64,61 @@ namespace Tango.Web.Controllers
{
code = HttpStatusCode.BadRequest;
}
- else if (ex is AuthenticationException)
+ else if (ex is AuthenticationException || ex is TokenExpiredException)
{
code = HttpStatusCode.Unauthorized;
}
+ var httpException = new HttpResponseException(Request.CreateErrorResponse(code, ex));
+
#if DEBUG
- throw new HttpResponseException(Request.CreateErrorResponse(code, ex.ToString()));
+ throw httpException;
#else
- throw new HttpResponseException(Request.CreateErrorResponse(code, ex.FlattenMessage()));
+ //Remove Stack trace
+ var expandedException = httpException.Response.Content as System.Net.Http.ObjectContent<System.Web.Http.HttpError>;
+
+ if (expandedException != null)
+ {
+ var expandedExceptionValues = expandedException.Value as HttpError;
+
+ if (expandedExceptionValues != null)
+ {
+ expandedExceptionValues["StackTrace"] = "StackTrace not provided.";
+ }
+ }
#endif
+
+
+ throw httpException;
+ }
+ }
+
+ protected virtual void OnRequestArrived(HttpRequestMessage request)
+ {
+ //Do nothing.
+ }
+ }
+
+ public class TangoController<T> : TangoController where T : class
+ {
+ public WebToken<T> RequestToken { get; set; }
+
+ protected override void OnRequestArrived(HttpRequestMessage request)
+ {
+ base.OnRequestArrived(request);
+
+ var authorizationHeader = request.Headers.Authorization;
+
+ if (authorizationHeader != null && authorizationHeader.Parameter != null)
+ {
+ try
+ {
+ RequestToken = WebToken<T>.FromToken(authorizationHeader.Parameter);
+ }
+ catch (Exception ex)
+ {
+ throw new HttpParseException("Could not parse the provided token embedded object.", ex);
+ }
}
}
}
diff --git a/Software/Visual_Studio/Tango.Web/Tango.Web.csproj b/Software/Visual_Studio/Tango.Web/Tango.Web.csproj
index 56711df28..961ec04e8 100644
--- a/Software/Visual_Studio/Tango.Web/Tango.Web.csproj
+++ b/Software/Visual_Studio/Tango.Web/Tango.Web.csproj
@@ -48,6 +48,9 @@
<Reference Include="Google.Protobuf, Version=3.4.1.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Protobuf.3.4.1\lib\net45\Google.Protobuf.dll</HintPath>
</Reference>
+ <Reference Include="JWT, Version=5.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\JWT.5.0.0\lib\net46\JWT.dll</HintPath>
+ </Reference>
<Reference Include="Microsoft.AI.Agent.Intercept, Version=2.0.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.0.6\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
</Reference>
@@ -186,13 +189,14 @@
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAzure.Storage, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
- <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
- <HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
+ <Reference Include="System.IdentityModel" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
@@ -257,7 +261,7 @@
<Compile Include="TangoWebApplication.cs" />
<Compile Include="TangoWebClient.cs" />
<Compile Include="WebConfig.cs" />
- <Compile Include="Controllers\JsonController.cs" />
+ <Compile Include="Controllers\TangoController.cs" />
<Compile Include="Formatters\JsonNetFormatter.cs" />
<Compile Include="Helpers\ObservablesContextHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -268,8 +272,12 @@
<Compile Include="WebSettings.cs" />
</ItemGroup>
<ItemGroup>
- <None Include="app.config" />
- <None Include="packages.config" />
+ <None Include="app.config">
+ <SubType>Designer</SubType>
+ </None>
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tango.BL\Tango.BL.csproj">
diff --git a/Software/Visual_Studio/Tango.Web/TangoWebClient.cs b/Software/Visual_Studio/Tango.Web/TangoWebClient.cs
index bb943533a..42a9d801c 100644
--- a/Software/Visual_Studio/Tango.Web/TangoWebClient.cs
+++ b/Software/Visual_Studio/Tango.Web/TangoWebClient.cs
@@ -15,8 +15,8 @@ namespace Tango.Web
private bool _disposed;
private TLoginRequest _lastLoginRequest;
- private DeploymentSlot _environment;
- public DeploymentSlot Environment
+ private DeploymentSlot? _environment;
+ public DeploymentSlot? Environment
{
get { return _environment; }
set
@@ -31,26 +31,40 @@ namespace Tango.Web
}
}
+ public String Address { get; set; }
public String Controller { get; private set; }
+ public String Token { get; private set; }
public WebToken WebToken { get; private set; }
public bool IsAuthenticated { get; private set; }
public TangoWebClient(DeploymentSlot environment, String controller)
{
+ _client = new WebTransportClient();
Controller = controller;
Environment = environment;
- _client = new WebTransportClient();
}
- public TangoWebClient(DeploymentSlot environment, String controller, WebToken token) : this(environment, controller)
+ public TangoWebClient(DeploymentSlot environment, String controller, String token) : this(environment, controller)
{
- WebToken = token;
+ Token = token;
+ }
+
+ public TangoWebClient(String address, String controller, String token)
+ {
+ _client = new WebTransportClient();
+ Address = address;
+ Controller = controller;
+ Token = token;
}
public async Task<TLoginResponse> Login(TLoginRequest request)
{
var response = await _client.PostJson<TLoginRequest, TLoginResponse>(GetActionAddress("Login"), request);
- WebToken = response.WebToken;
+ Token = response.AccessToken;
+ _client.AuthenticationToken = Token;
+
+ WebToken = WebToken.FromToken(Token);
+
_lastLoginRequest = request;
IsAuthenticated = true;
return response;
@@ -58,27 +72,11 @@ namespace Tango.Web
protected virtual async Task<TResponse> Post<TRequest, TResponse>(String action, TRequest request) where TRequest : class, IWebRequestMessage where TResponse : class, IWebResponseMessage
{
- bool has_own_token = false;
-
- if (request is WebRequestSecureMessage)
+ if (IsAuthenticated)
{
- has_own_token = (request as WebRequestSecureMessage).AccessToken != null;
-
- if (!has_own_token)
+ if (DateTime.UtcNow >= WebToken.Expiration)
{
- if (IsAuthenticated)
- {
- if (DateTime.UtcNow >= WebToken.Expiration)
- {
- await Login(_lastLoginRequest);
- }
- }
- else
- {
- throw new AuthenticationException("This tango web client has not been authenticated with the service. Please use the login method to authenticate.");
- }
-
- (request as WebRequestSecureMessage).AccessToken = WebToken.AccessToken;
+ await Login(_lastLoginRequest);
}
}
@@ -87,28 +85,15 @@ namespace Tango.Web
var response = await _client.PostJson<TRequest, TResponse>(GetActionAddress(action), request);
return response;
}
- catch (SessionExpiredException)
+ catch (TokenExpiredException)
{
- if (!has_own_token)
+ try
{
- try
- {
- await Login(_lastLoginRequest);
-
- if (request is WebRequestSecureMessage)
- {
- (request as WebRequestSecureMessage).AccessToken = WebToken.AccessToken;
- }
-
- var response = await _client.PostJson<TRequest, TResponse>(GetActionAddress(action), request);
- return response;
- }
- catch
- {
- throw;
- }
+ await Login(_lastLoginRequest);
+ var response = await _client.PostJson<TRequest, TResponse>(GetActionAddress(action), request);
+ return response;
}
- else
+ catch
{
throw;
}
@@ -126,7 +111,14 @@ namespace Tango.Web
protected virtual String GetServiceAddress()
{
- return Environment.ToAddress() + $"/api/{Controller}/";
+ if (Environment != null)
+ {
+ return Environment.Value.ToAddress() + $"/api/{Controller}/";
+ }
+ else
+ {
+ return Address + $"/api/{Controller}/";
+ }
}
public virtual void Dispose()
diff --git a/Software/Visual_Studio/Tango.Web/app.config b/Software/Visual_Studio/Tango.Web/app.config
index 63664236a..bc38de989 100644
--- a/Software/Visual_Studio/Tango.Web/app.config
+++ b/Software/Visual_Studio/Tango.Web/app.config
@@ -8,7 +8,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
+ <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -30,6 +30,10 @@
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" />
</dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Reactive.Core" publicKeyToken="94bc3704cddfc263" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-3.0.3000.0" newVersion="3.0.3000.0" />
+ </dependentAssembly>
</assemblyBinding>
</runtime>
<entityFramework>
diff --git a/Software/Visual_Studio/Tango.Web/packages.config b/Software/Visual_Studio/Tango.Web/packages.config
index cb6027500..0a940a2ea 100644
--- a/Software/Visual_Studio/Tango.Web/packages.config
+++ b/Software/Visual_Studio/Tango.Web/packages.config
@@ -3,6 +3,7 @@
<package id="Antlr" version="3.4.1.9004" targetFramework="net461" />
<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
<package id="Google.Protobuf" version="3.4.1" targetFramework="net461" />
+ <package id="JWT" version="5.0.0" targetFramework="net461" />
<package id="Microsoft.ApplicationInsights" version="2.2.0" targetFramework="net461" />
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.0.6" targetFramework="net461" />
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.2.0" targetFramework="net461" />
@@ -29,7 +30,7 @@
<package id="Microsoft.SqlServer.SqlManagementObjects" version="140.17283.0" targetFramework="net461" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net461" />
<package id="Modernizr" version="2.6.2" targetFramework="net461" />
- <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net461" />
+ <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="Respond" version="1.2.0" targetFramework="net461" />
<package id="System.Spatial" version="5.6.4" targetFramework="net461" />
<package id="WebGrease" version="1.5.2" targetFramework="net461" />