diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2019-02-20 22:55:15 +0200 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2019-02-20 22:55:15 +0200 |
| commit | 9447a8a09f87d6ea2cb62860021c595386668eec (patch) | |
| tree | a02db15a1247587f14fedb6ccae76f79bd63afb3 /Software/Visual_Studio/Tango.Web | |
| parent | 17446569ca8d8dd00331da5926b938593c4b117f (diff) | |
| download | Tango-9447a8a09f87d6ea2cb62860021c595386668eec.tar.gz Tango-9447a8a09f87d6ea2cb62860021c595386668eec.zip | |
A lot of work !!!
Diffstat (limited to 'Software/Visual_Studio/Tango.Web')
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/Authentication/TokensManager.cs | 6 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/Authentication/WebToken.cs | 151 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/Authentication/WebTokenResponse.cs | 2 | ||||
| -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.csproj | 18 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/TangoWebClient.cs | 82 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/app.config | 6 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.Web/packages.config | 3 |
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" /> |
