public Task <HttpResponseMessage> HandleRouteAsync(Type controllerType, IApplication httpApp, HttpRequestMessage request, string routeName, RouteHandlingDelegate continueExecution) { return(EastFive.Azure.AppSettings.TableInformationToken.ConfigurationString( async headerToken => { if (!request.Headers.Contains(HeaderKey)) { return await continueExecution(controllerType, httpApp, request, routeName); } if (request.Headers.GetValues(HeaderKey).First() != headerToken) { return request.CreateResponse(System.Net.HttpStatusCode.Unauthorized); } if (request.Headers.Contains("Migrate")) { var tableData = await controllerType.StorageGetAll().ToArrayAsync(); return request.CreateResponse(System.Net.HttpStatusCode.OK, tableData); } var tableInformation = await controllerType.StorageTableInformationAsync(); return request.CreateResponse(System.Net.HttpStatusCode.OK, tableInformation); }, why => continueExecution(controllerType, httpApp, request, routeName))); }
public Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest request, RouteHandlingDelegate continueExecution) { if (!request.Headers.ContainsKey(HeaderKey)) { return(continueExecution(controllerType, httpApp, request)); } return(EastFive.Azure.AppSettings.TableInformationToken.ConfigurationString( async headerToken => { if (request.Headers[HeaderKey].First() != headerToken) { return request.CreateResponse(System.Net.HttpStatusCode.Unauthorized); } if (request.Headers.ContainsKey("X-StorageTableInformation-List")) { var tableData = await controllerType.StorageGetAll().ToArrayAsync(); return request.CreateExtrudedResponse( System.Net.HttpStatusCode.OK, tableData); } if (request.Headers.ContainsKey("X-StorageTableInformation-RepairModifiers")) { var query = request.Headers .Where(hdr => "X-StorageTableInformation-Query".Equals(hdr.Key, StringComparison.OrdinalIgnoreCase)) .First( (hdr, next) => hdr.Value.First( (hdrValue, next) => hdrValue, () => string.Empty), () => string.Empty); return new WriteStreamAsyncHttpResponse(request, System.Net.HttpStatusCode.OK, $"{controllerType.FullName}.repair.txt", "text/text", true, async stream => { string [] repairs = await controllerType .StorageRepairModifiers(query) .Select( async line => { var bytes = line.GetBytes(Encoding.UTF8); await stream.WriteAsync(bytes, 0, bytes.Length); return line; }) .Await(readAhead: 100) .ToArrayAsync(); }); } var tableInformation = await controllerType.StorageTableInformationAsync(); return request.CreateResponse(System.Net.HttpStatusCode.OK, tableInformation); }, why => continueExecution(controllerType, httpApp, request))); }
public Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest request, RouteHandlingDelegate continueExecution) { Func <Task <IHttpResponse> > skip = () => continueExecution(controllerType, httpApp, request); if (!AppSettings.CorsCorrection.ConfigurationBoolean(s => s, onNotSpecified: () => false)) { return(skip()); } // Configs to set: // // EastFive.Api.CorsCorrection=true // cors:Origins=https://myserver.com,etc. (localhost included by default so this app setting can remain absent/unconfigured if just need localhost) // cors:MaxAgeSeconds=60 (default is 5 seconds if not set) // return(GetResponse()); string GetAllowedOrigin() { // accept localhost (all ports) // accept request server // accept any additional servers in config request.Headers.TryGetValue("Origin", out string[] reqOrigins); var localhostAuthorities = reqOrigins .NullToEmpty() .SelectMany(reqOrigin => reqOrigin.Split(','.AsArray(), StringSplitOptions.RemoveEmptyEntries)) .Where( (reqOrigin) => { if (!Uri.TryCreate(reqOrigin, UriKind.Absolute, out Uri reqOriginUri)) { return(false); } return(reqOriginUri.GetLeftPart(UriPartial.Authority).IndexOf("localhost", StringComparison.OrdinalIgnoreCase) != -1); }); var requestAuthority = request.RequestUri.GetLeftPart(UriPartial.Authority); var corsAuthorities = "cors:Origins".ConfigurationString( (v) => v.Split(','.AsArray(), StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray(), (why) => new string[] { }); var allowableOriginValues = localhostAuthorities .Append(requestAuthority) .Concat(corsAuthorities) .Distinct() .ToArray(); var allowedOrigin = allowableOriginValues .NullToEmpty() .First( (allowed, next) => { if (!reqOrigins.NullToEmpty().Contains(allowed, StringComparer.OrdinalIgnoreCase)) { return(next()); } return(allowed); }, () => default(string)); return(allowedOrigin); } string GetAllowedMethods() { // accept OPTIONS // accept any additional methods in config request.Headers.TryGetValue("Access-Control-Request-Method", out string[] reqMethod); return(reqMethod .NullToEmpty() .Append("OPTIONS") .Distinct() .Join(",")); } string GetAllowedHeaders() { // accept any headers requested request.Headers.TryGetValue("Access-Control-Request-Headers", out string[] reqHeaders); return(reqHeaders .NullToEmpty() .Distinct() .Join(",")); } string GetMaxAgeSeconds() { return("cors:MaxAgeSeconds" .ConfigurationLong( (v) => v, (why) => 5, () => 5) .ToString()); } Task <IHttpResponse> GetResponse() { if (request.Method.Method.ToLower() != HttpMethod.Options.Method.ToLower()) { return(skip()); } var allowedOrigin = GetAllowedOrigin(); if (allowedOrigin == default) { return(request.CreateResponse(System.Net.HttpStatusCode.Forbidden).AddReason("origin not allowed").AsTask()); } var response = request.CreateResponse(System.Net.HttpStatusCode.OK); response.SetHeader("Access-Control-Allow-Origin", allowedOrigin); response.SetHeader("Access-Control-Allow-Methods", GetAllowedMethods()); response.SetHeader("Access-Control-Allow-Headers", GetAllowedHeaders()); response.SetHeader("Vary", "origin"); response.SetHeader("Access-Control-Max-Age", GetMaxAgeSeconds()); return(response.AsTask()); } }
public async Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest request, RouteHandlingDelegate continueExecution) { var response = await continueExecution(controllerType, httpApp, request); if (deactivated) { return(response); } string teamsNotifyParam = GetTeamsNotifyParameter(); if (!ShouldNotify(out string collectionFolder)) { return(response); } try { string messageId = await TeamsNotifyAsync(controllerType, resourceInvoker, httpApp, request, response, teamsNotifyParam, collectionFolder); } catch (HttpRequestException) { } catch (Exception) { } return(response); string GetTeamsNotifyParameter() { return(request.Headers .Where(kvp => kvp.Key.Equals("X-Teams-Notify", StringComparison.OrdinalIgnoreCase)) .First( (teamsNotifyParams, next) => { return teamsNotifyParams.Value.First( (teamsNotifyParam, next) => teamsNotifyParam, () => default(string)); }, () => { return default(string); })); } bool HasReportableError() { if (((int)response.StatusCode) < 400) { return(false); } if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { return(false); } if (response.StatusCode == System.Net.HttpStatusCode.Conflict) { return(false); } return(true); } bool RequestTeamsNotify() => teamsNotifyParam != default; bool ShouldNotify(out string collectionFolder) { collectionFolder = default; if (RequestTeamsNotify()) { return(true); } if (HasReportableError()) { return(true); } if (TeamsNotification.IsMatch(request, response, out collectionFolder)) { return(true); } return(false); } }
public Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest request, RouteHandlingDelegate continueExecution) { if (!request.RequestUri.TryGetQueryParam(ParameterName, out string apiVoucher)) { if (!request.TryGetHeader(ParameterName, out apiVoucher)) { return(continueExecution(controllerType, httpApp, request)); } } if (request.TryGetAuthorization(out string auth)) { return(continueExecution(controllerType, httpApp, request)); } return(EastFive.Security.VoucherTools.ValidateUrlToken(apiVoucher, async(voucherTokenId) => { return await await voucherTokenId.AsRef <VoucherToken>() .StorageGetAsync( voucherToken => { return EastFive.Security.AppSettings.TokenScope.ConfigurationUri( scope => { var tokenExpiration = TimeSpan.FromMinutes(1.0); request.RequestUri = request.RequestUri.RemoveQueryParameter("api-voucher"); var sessionId = apiVoucher.MD5HashGuid(); var claims = voucherToken.claims; return JwtTools.CreateToken(sessionId, scope, tokenExpiration, claims, (tokenNew) => { request.SetAuthorization(tokenNew); return continueExecution(controllerType, httpApp, request); }, (missingConfig) => continueExecution(controllerType, httpApp, request), (configName, issue) => continueExecution(controllerType, httpApp, request)); }, (why) => continueExecution(controllerType, httpApp, request)); }, () => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason("Voucher token does not exist.") .AsTask()); }, why => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason(why) .AsTask(), why => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason(why) .AsTask(), why => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason(why) .AsTask(), why => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason(why) .AsTask(), (name, why) => request .CreateResponse(System.Net.HttpStatusCode.Unauthorized) .AddReason(why) .AsTask())); }
public async Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest routeData, RouteHandlingDelegate continueExecution) { var stopwatch = Stopwatch.StartNew(); var requestId = Guid.NewGuid().ToString("N"); var telemetry = new RequestTelemetry() { Id = requestId, Source = controllerType.Assembly.FullName, Timestamp = DateTimeOffset.UtcNow, Url = routeData.GetAbsoluteUri(), // request.RequestUri, }; #region User / Session var claims = routeData.GetClaims( claimsEnumerable => claimsEnumerable.ToArray(), () => new Claim[] { }, (why) => new Claim[] { }); var sessionIdClaimType = Api.Auth.ClaimEnableSessionAttribute.Type; var sessionIdMaybe = SessionToken.GetClaimIdMaybe(claims, sessionIdClaimType); if (sessionIdMaybe.HasValue) { telemetry.Context.Session.Id = sessionIdMaybe.Value.ToString().ToUpper(); } var accountIdClaimType = EastFive.Api.AppSettings.ActorIdClaimType.ConfigurationString( (accIdCT) => accIdCT, (why) => default); if (accountIdClaimType.HasBlackSpace()) { var accountIdMaybe = SessionToken.GetClaimIdMaybe(claims, accountIdClaimType); if (accountIdMaybe.HasValue) { var accountIdStr = accountIdMaybe.Value.ToString().ToUpper(); telemetry.Context.User.AccountId = accountIdStr; telemetry.Context.User.AuthenticatedUserId = accountIdStr; } } foreach (var claim in claims.Distinct(claim => claim.Type)) { telemetry.Properties.Add($"claim[{claim.Type}]", claim.Value); } #endregion routeData.Properties.Add(HttpRequestMessagePropertyRequestTelemetryKey, telemetry); var response = await continueExecution(controllerType, httpApp, routeData); telemetry.ResponseCode = response.StatusCode.ToString(); if (response.ReasonPhrase.HasBlackSpace()) { telemetry.Properties.AddOrReplace("reason_phrase", response.ReasonPhrase); } telemetry.Success = response.StatusCode.IsSuccess(); #region Method result identfiers if (response.Headers.TryGetValue(Middleware.HeaderStatusType, out string[] statusNames))
public async Task <HttpResponseMessage> HandleRouteAsync(Type controllerType, IApplication httpApp, HttpRequestMessage request, string routeName, RouteHandlingDelegate continueExecution) { var stopwatch = Stopwatch.StartNew(); var requestId = Guid.NewGuid().ToString("N"); var telemetry = new RequestTelemetry() { Id = requestId, Source = "EastFive.Api", Timestamp = DateTimeOffset.UtcNow, Url = request.RequestUri, }; #region User / Session var claims = request.GetClaims( claimsEnumerable => claimsEnumerable.ToArray(), () => new Claim[] { }, (why) => new Claim[] { }); var sessionIdClaimType = BlackBarLabs.Security.ClaimIds.Session; var sessionIdMaybe = SessionToken.GetClaimIdMaybe(claims, sessionIdClaimType); if (sessionIdMaybe.HasValue) { telemetry.Context.Session.Id = sessionIdMaybe.Value.ToString().ToUpper(); } var accountIdClaimType = EastFive.Api.AppSettings.ActorIdClaimType.ConfigurationString( (accIdCT) => accIdCT, (why) => default); if (accountIdClaimType.HasBlackSpace()) { var accountIdMaybe = SessionToken.GetClaimIdMaybe(claims, accountIdClaimType); if (accountIdMaybe.HasValue) { var accountIdStr = accountIdMaybe.Value.ToString().ToUpper(); telemetry.Context.User.AccountId = accountIdStr; telemetry.Context.User.AuthenticatedUserId = accountIdStr; } } foreach (var claim in claims) { telemetry.Properties.Add($"claim[{claim.Type}]", claim.Value); } #endregion request.Properties.Add(HttpRequestMessagePropertyRequestTelemetryKey, telemetry); var response = await continueExecution(controllerType, httpApp, request, routeName); telemetry.ResponseCode = response.StatusCode.ToString(); if (response.ReasonPhrase.HasBlackSpace()) { telemetry.Properties.AddOrReplace("reason_phrase", response.ReasonPhrase); } telemetry.Success = response.IsSuccessStatusCode; #region Method result identfiers if (response.Headers.TryGetValues(HeaderStatusName, out IEnumerable <string> statusNames)) { if (statusNames.Any()) { telemetry.Properties.Add(TelemetryStatusName, statusNames.First()); } } if (response.Headers.TryGetValues(HeaderStatusInstance, out IEnumerable <string> statusInstances)) { if (statusInstances.Any()) { telemetry.Properties.Add(TelemetryStatusInstance, statusInstances.First()); } } #endregion var telemetryClient = AppSettings.ApplicationInsights.InstrumentationKey.LoadTelemetryClient(); telemetry.Duration = stopwatch.Elapsed; telemetryClient.TrackRequest(telemetry); return(response); }
public Task <IHttpResponse> HandleRouteAsync(Type controllerType, IInvokeResource resourceInvoker, IApplication httpApp, IHttpRequest request, RouteHandlingDelegate continueExecution) { if (!request.RequestUri.TryGetQueryParam( AccessTokenAccountExtensions.QueryParameter, out string accessToken)) { return(continueExecution(controllerType, httpApp, request)); } if (request.GetAuthorization().HasBlackSpace()) { return(continueExecution(controllerType, httpApp, request)); } return(request.RequestUri.ValidateAccessTokenAccount( accessTokenInfo => { return EastFive.Security.AppSettings.TokenScope.ConfigurationUri( scope => { var tokenExpiration = TimeSpan.FromMinutes(1.0); request.RequestUri = request.RequestUri.RemoveQueryParameter( AccessTokenAccountExtensions.QueryParameter); var sessionId = accessTokenInfo.sessionId; var authId = accessTokenInfo.accountId; var duration = accessTokenInfo.expirationUtc - DateTime.UtcNow; return JwtTools.CreateToken(sessionId, authId, scope, duration, tokenCreated: (tokenNew) => { request.SetAuthorization(tokenNew); return continueExecution(controllerType, httpApp, request); }, missingConfigurationSetting: (configName) => continueExecution(controllerType, httpApp, request), invalidConfigurationSetting: (configName, issue) => continueExecution(controllerType, httpApp, request)); }, (why) => continueExecution(controllerType, httpApp, request), () => continueExecution(controllerType, httpApp, request)); }, onAccessTokenNotProvided: () => continueExecution(controllerType, httpApp, request), onAccessTokenInvalid: () => { return request .CreateResponse(System.Net.HttpStatusCode.Forbidden) .AddReason("Access token is invalid") .AsTask(); }, onAccessTokenExpired: () => { return request .CreateResponse(System.Net.HttpStatusCode.Forbidden) .AddReason("Access token is expired") .AsTask(); }, onInvalidSignature: () => { return request .CreateResponse(System.Net.HttpStatusCode.Forbidden) .AddReason("Access token has an invalid signature") .AsTask(); }, onSystemNotConfigured: () => continueExecution(controllerType, httpApp, request))); }