Advertisement
Guest User

Untitled

a guest
Jan 24th, 2018
716
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 18.64 KB | None | 0 0
  1. using System;
  2. using System.Linq;
  3. using System.Security.Claims;
  4. using System.Threading.Tasks;
  5. using System.Web.Http;
  6. using System.Web.Http.Dispatcher;
  7. using Microsoft.ApplicationInsights.Extensibility;
  8. using Microsoft.Azure.ActiveDirectory.GraphClient;
  9. using Microsoft.IdentityModel.Clients.ActiveDirectory;
  10. using Microsoft.Owin;
  11. using Microsoft.Owin.Security;
  12. using Microsoft.Owin.Security.Cookies;
  13. using Microsoft.Owin.Security.OpenIdConnect;
  14. using Microsoft.Practices.Unity;
  15. using MSCorp.Immersion.Api.Infrastructure.Authentication;
  16. using MSCorp.Immersion.Api.Infrastructure.Authorization;
  17. using MSCorp.Immersion.Api.Infrastructure.Configuration;
  18. using MSCorp.Immersion.Api.Infrastructure.Logging;
  19. using MSCorp.Immersion.Api.Models;
  20. using MSCorp.Immersion.Api.Services.Interfaces;
  21. using MSCorp.Immersion.Data;
  22. using Newtonsoft.Json;
  23. using Owin;
  24. using Thinktecture.IdentityModel.Owin;
  25. using AuthenticationTypes = MSCorp.Immersion.Api.Infrastructure.AuthenticationTypes;
  26.  
  27. using static MSCorp.Immersion.Api.Infrastructure.Logging.ApiEventSource;
  28.  
  29. [assembly: OwinStartup(typeof(MSCorp.Immersion.Api.Startup))]
  30.  
  31. namespace MSCorp.Immersion.Api
  32. {
  33.     public partial class Startup
  34.     {
  35.         public void Configuration(IAppBuilder app)
  36.         {
  37.             ConfigureLogging();
  38.             ConfigureDatabase();
  39.             ConfigureAuth(app);
  40.  
  41.             // The public-facing V1 Api
  42.             app.Map("/api/v1", ConfigureV1Api);
  43.  
  44.             // Api which serves the website only
  45.             app.Map("/api/internal", ConfigureInternalApi);
  46.         }
  47.  
  48.         private static void ConfigureLogging()
  49.         {
  50.             TelemetryConfiguration.Active.SetInstrumentationKeyFromConfig();
  51.             LoggingConfiguration.ConfigureEventTracing();
  52.         }
  53.  
  54.         private static void ConfigureDatabase()
  55.         {
  56.             DatabaseInitializer.SetDefault();
  57.         }
  58.  
  59.         private static void ConfigureV1Api(IAppBuilder app)
  60.         {
  61.             // NOTE(benf): Create as child container to allow api-only container customization
  62.             var container = UnityConfiguration.Container.CreateChildContainer();
  63.  
  64.             container.RegisterInstance<IHttpControllerTypeResolver>(
  65.                 new DefaultHttpControllerTypeResolver(
  66.                     predicate: t => t.Namespace?.StartsWith("MSCorp.Immersion.Api.Controllers.Api.V1") ?? false));
  67.  
  68.             AddApiKeyAuth(app, StaticConfiguration.AuthenticationApiKey, StaticConfiguration.AuthenticationApiSecret, Roles.ApiRole);
  69.             AddApiKeyAuth(app, StaticConfiguration.AuthenticationTelemetryKey, StaticConfiguration.AuthenticationTelemetrySecret, Roles.TelemetryRole);
  70.  
  71.             var config = new HttpConfiguration();
  72.             config.EnableApplicationInsights();
  73.             config.MapHttpAttributeRoutes();
  74.             config.ConfigureSsl();
  75.             config.ConfigureFormatting();
  76.             config.ConfigureErrorHandling();
  77.             config.DependencyResolver = new UnityResolver(container);
  78.             config.Filters.Add(new AuthorizeAttribute());
  79.  
  80.             app.UseWebApi(config);
  81.  
  82.             // NOTE(benf): ensure that all requests to the /api/v1 endpoint are handled in the event of WebAPI failing to map the request
  83.             UseHandlerNotFound(app);
  84.         }
  85.  
  86.         private static void AddApiKeyAuth(IAppBuilder app, string apiKey, string apiSecret, string apiRole)
  87.         {
  88.             app.UseBasicAuthentication(new BasicAuthenticationOptions("Web", (key, secret) =>
  89.                 Task.FromResult(string.Equals(apiKey, key, StringComparison.Ordinal) &&
  90.                                 string.Equals(apiSecret, secret, StringComparison.Ordinal)
  91.                     ? new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, key), new Claim(ClaimsIdentity.DefaultRoleClaimType, apiRole) }.AsEnumerable()
  92.                     : null)));
  93.         }
  94.  
  95.         private static void ConfigureInternalApi(IAppBuilder app)
  96.         {
  97.             // NOTE(benf): Create as child container to allow api-only container customization
  98.             var container = UnityConfiguration.Container.CreateChildContainer();
  99.  
  100.             container.RegisterInstance<IHttpControllerTypeResolver>(
  101.                 new DefaultHttpControllerTypeResolver(
  102.                     predicate: t => t.Namespace?.StartsWith("MSCorp.Immersion.Api.Controllers.Api.Internal") ?? false));
  103.  
  104.             var config = new HttpConfiguration();
  105.             config.EnableApplicationInsights();
  106.             config.MapHttpAttributeRoutes();
  107.             config.ConfigureSsl();
  108.             config.ConfigureFormatting();
  109.             config.ConfigureWebApiThrottle();
  110.             config.ConfigureErrorHandling();
  111.             config.DependencyResolver = new UnityResolver(container);
  112.             config.Filters.Add(new AuthorizeAttribute());
  113.  
  114.             app.UseWebApi(config);
  115.  
  116.             // NOTE(benf): ensure that all requests to the /api/internal endpoint are handled in the event of WebAPI failing to map the request
  117.             UseHandlerNotFound(app);
  118.         }
  119.  
  120.         private static void ConfigureAuth(IAppBuilder app)
  121.         {
  122.             app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
  123.             app.UseCookieAuthentication(new CookieAuthenticationOptions());
  124.  
  125.             // MICROSOFT EMPLOYEE AUTHENTICATION
  126.             app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions(AuthenticationTypes.Aad)
  127.             {
  128.                 ClientId = StaticConfiguration.AuthenticationAadClientId,
  129.                 Authority = $"https://login.microsoftonline.com/{StaticConfiguration.AuthenticationAadTenantId}",
  130.                 PostLogoutRedirectUri = StaticConfiguration.AuthenticationPostLogoutRedirectUri,
  131.                 Notifications = new OpenIdConnectAuthenticationNotifications
  132.                 {
  133.                     AuthorizationCodeReceived = async context =>
  134.                     {
  135.                         await InitializeUserEmailAsync(context.AuthenticationTicket.Identity, new Uri(StaticConfiguration.AuthenticationPostLogoutRedirectUri), context.Code);
  136.                         await InitializeUserAsync(context.AuthenticationTicket.Identity, false);
  137.                     },
  138.                     RedirectToIdentityProvider = context =>
  139.                     {
  140.                         context.ProtocolMessage.DomainHint = StaticConfiguration.AuthenticationAadDomainHint;
  141.                         return Task.FromResult(0);
  142.                     },
  143.                     SecurityTokenValidated = context =>
  144.                     {
  145.                         Log.AadSignInComplete(context.AuthenticationTicket.Identity.Name);
  146.  
  147.                         context.AuthenticationTicket.Identity.AddClaim(new Claim(Claims.AuthenticationType, AuthenticationTypes.Aad));
  148.                         context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.PartnerRole));
  149.                         context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.EmployeeRole));
  150.                         context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.RegisteredRole));
  151.  
  152.                         return Task.FromResult(0);
  153.                     },
  154.                     AuthenticationFailed = context =>
  155.                     {
  156.                         Log.AadSignInFailed(context.Exception);
  157.  
  158.                         context.HandleResponse();
  159.                         context.Response.Redirect($"{StaticConfiguration.BaseSiteUrl}/Account/Unauthorized");
  160.                         return Task.FromResult(0);
  161.                     }
  162.                 }
  163.             });
  164.  
  165.             //#if DEBUG
  166.             //            // STUB USER AUTHENTICATION
  167.             //            app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
  168.             //            {
  169.             //                Path = "/account/signin/customer",
  170.             //                Claims = new[] {
  171.             //                    new Claim(ClaimsIdentity.DefaultNameClaimType, "00037FFEB573ED28"),
  172.             //                    new Claim(ClaimTypes.GivenName, "James"),
  173.             //                    new Claim(ClaimTypes.Surname, "Customer"),
  174.             //                    new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
  175.             //                    new Claim(ClaimTypes.Email, "a@b.com"),
  176.             //                    new Claim(Claims.ClientId, "i"),
  177.             //                    new Claim(ClaimTypes.Role, Roles.RegisteredRole)
  178.             //                }
  179.             //            });
  180.  
  181.             //            app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
  182.             //            {
  183.             //                Path = "/account/signin/partner",
  184.             //                Claims = new[] {
  185.             //                    new Claim(ClaimsIdentity.DefaultNameClaimType, "00037FFEB573ED29"),
  186.             //                    new Claim(ClaimTypes.GivenName, "James"),
  187.             //                    new Claim(ClaimTypes.Surname, "Partner"),
  188.             //                    new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
  189.             //                    new Claim(ClaimTypes.Email, "a@b.com"),
  190.             //                    new Claim(Claims.ClientId, "i"),
  191.             //                    new Claim(ClaimTypes.Role, Roles.RegisteredRole),
  192.             //                    new Claim(ClaimTypes.Role, Roles.PartnerRole),
  193.             //                    new Claim(ClaimTypes.Role, Roles.ContentAdminRole),
  194.             //                    new Claim(ClaimTypes.Role, Roles.BookingAdminRole),
  195.             //                    new Claim(ClaimTypes.Role, Roles.VirtualClassAdminRole)
  196.             //                }
  197.             //            });
  198.             // STUB USER AUTHENTICATION
  199.             app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
  200.             {
  201.                 Path = "/account/signin/customer",
  202.                 Claims = new[] {
  203.                     new Claim(ClaimsIdentity.DefaultNameClaimType, "000640008D4B2BC4"),
  204.                     new Claim(ClaimTypes.GivenName, "Jason"),
  205.                     new Claim(ClaimTypes.Surname, "Ciemiega"),
  206.                     new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
  207.                     new Claim(ClaimTypes.Email, "jciemiega@hotmail.com"),
  208.                     new Claim(Claims.ClientId, "i"),
  209.                     new Claim(ClaimTypes.Role, Roles.RegisteredRole)
  210.                 }
  211.             });
  212.  
  213.             app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
  214.             {
  215.                 Path = "/account/signin/partner",
  216.                 Claims = new[] {
  217.                     new Claim(ClaimsIdentity.DefaultNameClaimType, "000640008D4B2BC4"),
  218.                     new Claim(ClaimTypes.GivenName, "Jason"),
  219.                     new Claim(ClaimTypes.Surname, "Ciemiega"),
  220.                     new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
  221.                     new Claim(ClaimTypes.Email, "jciemiega@hotmail.com"),
  222.                     new Claim(Claims.ClientId, "i"),
  223.                     new Claim(ClaimTypes.Role, Roles.RegisteredRole),
  224.                     new Claim(ClaimTypes.Role, Roles.PartnerRole),
  225.                     new Claim(ClaimTypes.Role, Roles.ContentAdminRole),
  226.                     new Claim(ClaimTypes.Role, Roles.BookingAdminRole),
  227.                     new Claim(ClaimTypes.Role, Roles.VirtualClassAdminRole)
  228.                 }
  229.             });
  230.             //#else
  231.             // REAL USER AUTHENTICATION
  232.             //app.Use(typeof(RpsProxyAuthenticationMiddleware), new RpsProxyAuthenticationOptions(AuthenticationTypes.Msa)
  233.             //{
  234.             //    AuthenticationMode = AuthenticationMode.Passive,
  235.             //    Proxy = StaticConfiguration.AuthenticationRpsProxyUri,
  236.             //    Key = StaticConfiguration.AuthenticationRpsKey,
  237.             //    SecurityTokenValidated = async context =>
  238.             //    {
  239.             //        var isPartnerRequest = context.AuthenticationTicket.Properties.Dictionary.Any(x => x.Key == "p" && x.Value == "1");
  240.             //        await InitializeUserAsync(context.AuthenticationTicket.Identity, isPartnerRequest);
  241.             //        await InitializeUserProfileAsync(context.AuthenticationTicket.Identity);
  242.             //    },
  243.             //    AuthenticationFailed = context =>
  244.             //    {
  245.             //        context.HandleResponse();
  246.             //        context.Response.Redirect($"{StaticConfiguration.BaseSiteUrl}/Account/Unauthorized");
  247.             //        return Task.FromResult(0);
  248.             //    }
  249.             //});
  250. //#endif
  251.         }
  252.  
  253.         private static async Task InitializeUserAsync(ClaimsIdentity identity, bool isPartnerRequest)
  254.         {
  255.             using (var container = UnityConfiguration.Container.CreateChildContainer())
  256.             {
  257.                 var userService = container.Resolve<IUserService>();
  258.  
  259.                 bool userExists = await userService.GetUserByExternalIdAsync(identity) != null;
  260.  
  261.                 var user = await userService.AddOrUpdateUserAsync(identity);
  262.  
  263.                 //Send Marketo Lead when a new user is created, but don't do for MS sign in.
  264.                 if (user.AuthType != AuthenticationTypes.Aad && !userExists)
  265.                 {                  
  266.                     await InitializeMarketoLead(identity);
  267.                 }
  268.  
  269.                 if (user.Blocked.GetValueOrDefault())
  270.                 {
  271.                     throw new Exception($"User is blocked: {identity.Name}");
  272.                 }
  273.  
  274.                 // TODO(benf): Run this on first sign-in only?
  275.                 var sellerBookRepository = container.Resolve<ISellerBookService>();
  276.                
  277.                 // Link this user with any unlinked "facilitator" records in their name (email)
  278.                 await sellerBookRepository.UpdateSellerBookFacilitatorsUserId(user.Email, user.Id);
  279.  
  280.                 // If the user is a Microsoft Employee, also search on their External ID
  281.                 // NOTE(benf): ExternalId for employees is their Microsoft alias. Email will be friendly alias
  282.                 // e.g. Email = Joe.Bloggs@microsoft.com
  283.                 //      ExternalId = jbloggs@microsoft.com
  284.                 if (user.AuthType == AuthenticationTypes.Aad)
  285.                 {
  286.                     await sellerBookRepository.UpdateSellerBookFacilitatorsUserId(user.ExternalId, user.Id);
  287.                 }
  288.  
  289.                 var authService = container.Resolve<IAuthorizationService>();
  290.                 var claims = await authService.GetUserClaimsAsync(user.Id);
  291.  
  292.                 if (isPartnerRequest && StaticConfiguration.AuthorizationPvpEnabled)
  293.                 {
  294.                     var partnerClaims = await authService.GetUserPartnerClaimsAsync(user.Id);
  295.                     claims = claims.Concat(partnerClaims).ToList();
  296.                 }
  297.  
  298.                 claims.Add(new Claim(Claims.UserId, user.Id.ToString()));
  299.  
  300.                 // If the partner status is assigned by the database roles list, then only apply it when
  301.                 // the user is logging in as a partner
  302.                 foreach (var role in claims.Where(x => isPartnerRequest || !string.Equals(x.Value, Roles.PartnerRole, StringComparison.OrdinalIgnoreCase)))
  303.                 {
  304.                     identity.AddClaim(role);
  305.                 }
  306.             }
  307.         }
  308.  
  309.         private static async Task InitializeUserProfileAsync(ClaimsIdentity identity)
  310.         {
  311.             using (var container = UnityConfiguration.Container.CreateChildContainer())
  312.             {
  313.                 var userService = container.Resolve<IUserService>();
  314.                 var profileService = container.Resolve<IProfileRegistrationService>();
  315.                 var puid = identity.Claims.Single(x => x.Type == ClaimsIdentity.DefaultNameClaimType).Value;
  316.  
  317.                 var profile = await profileService.FindProfileAsync(new ProfilePuid {Puid = puid});
  318.                 if (profile != null)
  319.                 {
  320.                     var userIdClaim = identity.Claims.Single(x => x.Type == Claims.UserId);
  321.                     await userService.UpdateUserDetailsAsync(new Guid(userIdClaim.Value), profile.Country, profile.PreferredEmail);
  322.                     identity.AddClaim(new Claim(ClaimTypes.Role, Roles.RegisteredRole));
  323.                 }
  324.             }
  325.         }
  326.  
  327.         private static async Task InitializeMarketoLead(ClaimsIdentity identity)
  328.         {
  329.             try
  330.             {
  331.                 using (var container = UnityConfiguration.Container.CreateChildContainer())
  332.                 {
  333.                     var profileService = container.Resolve<IProfileRegistrationService>();
  334.  
  335.                     var puid = identity.Claims.Single(x => x.Type == ClaimsIdentity.DefaultNameClaimType).Value;
  336.                     await profileService.SendMarketoLeadForNewProfileAsync(new ProfilePuid { Puid = puid });
  337.                 }
  338.             }
  339.             catch (Exception ex)
  340.             {
  341.                 Log.MarketoIntializeFailed(ex);
  342.             }
  343.         }
  344.  
  345.         private static async Task InitializeUserEmailAsync(ClaimsIdentity identity, Uri redirectUri, string code)
  346.         {
  347.             var client = new ActiveDirectoryClient(new Uri($"https://graph.windows.net/{StaticConfiguration.AuthenticationAadTenantId}"),
  348.                 async () =>
  349.                 {
  350.                     var credential = new ClientCredential(StaticConfiguration.AuthenticationAadClientId, StaticConfiguration.AuthenticationAadClientKey);
  351.                     var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{StaticConfiguration.AuthenticationAadTenantId}", null);
  352.                     var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, credential, "https://graph.windows.net");
  353.  
  354.                     return result.AccessToken;
  355.                 });
  356.  
  357.             var me = await client.Me.ExecuteAsync();
  358.             if (!string.IsNullOrEmpty(me.Mail))
  359.             {
  360.                 identity.AddClaim(new Claim(ClaimTypes.Email, me.Mail));
  361.             }
  362.         }
  363.  
  364.         private static void UseHandlerNotFound(IAppBuilder app)
  365.         {
  366.             app.Use((ctx, next) =>
  367.             {
  368.                 ctx.Response.StatusCode = 404;
  369.                 ctx.Response.Headers.Set("Content-Type", "application/json");
  370.                 ctx.Response.Write(JsonConvert.SerializeObject(new { message = "Handler not found" }));
  371.                 return Task.FromResult(0);
  372.             });
  373.         }
  374.     }
  375. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement