From 7b4298dc2ab60d9462a89f75caf0883b5f1ba102 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Tue, 15 Mar 2022 22:10:12 -0700 Subject: [PATCH 01/48] Initial version. Needs a few updates to unit tests --- .../Microsoft.Identity.Web.xml | 3 +- src/Microsoft.Identity.Web/ITokenAcquirer.cs | 55 +++ .../ITokenAcquisition.cs | 23 +- .../ITokenAcquisitionHost.cs | 23 + .../ITokenAcquisitionInternal.cs | 31 +- src/Microsoft.Identity.Web/MergedOptions.cs | 122 +++--- .../Microsoft.Identity.Web.csproj | 23 +- .../Microsoft.Identity.Web.xml | 174 +++++--- .../MicrosoftIdentityOptions.cs | 46 +- .../ServiceCollectionExtensions.cs | 2 + .../TokenAcquisition-AspnetCore.cs | 173 ++++++++ .../TokenAcquisition.cs | 402 ++++-------------- .../TokenAcquisitionAspnetCoreHost.cs | 208 +++++++++ .../TokenAcquisitionOptions.cs | 13 + ...softIdentityWebAppAuthenticationBuilder.cs | 2 +- .../WebAppExtensionsTests.cs | 2 +- 16 files changed, 866 insertions(+), 436 deletions(-) create mode 100644 src/Microsoft.Identity.Web/ITokenAcquirer.cs create mode 100644 src/Microsoft.Identity.Web/ITokenAcquisitionHost.cs create mode 100644 src/Microsoft.Identity.Web/TokenAcquisition-AspnetCore.cs create mode 100644 src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs diff --git a/src/Microsoft.Identity.Web.TokenCache/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web.TokenCache/Microsoft.Identity.Web.xml index d8fe5df97..0973abe59 100644 --- a/src/Microsoft.Identity.Web.TokenCache/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web.TokenCache/Microsoft.Identity.Web.xml @@ -487,12 +487,13 @@ LoggingMessage class for MsalAbstractTokenCacheProvider. - + Cache deserialization error. ILogger. MSAL.NET cache key. + Whether cache is encrypted or not. Error message. Exception. diff --git a/src/Microsoft.Identity.Web/ITokenAcquirer.cs b/src/Microsoft.Identity.Web/ITokenAcquirer.cs new file mode 100644 index 000000000..c6d03a615 --- /dev/null +++ b/src/Microsoft.Identity.Web/ITokenAcquirer.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Identity.Client; + +namespace Microsoft.Identity.Web +{ + /// + /// Interface for the token acquisition service (encapsulating MSAL.NET). + /// + public interface ITokenAcquirer + { + /// + /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + /// for a downstream API on behalf of the user account for which the claims are provided in the + /// member of the controller's parameter. + /// + /// Scopes to request for the downstream API to call. + /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + /// Optional claims principal representing the user. If not provided, will use the signed-in + /// user (in a web app), or the user for which the token was received (in a web API) + /// cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. + /// An to call on behalf of the user, the downstream API characterized by its scopes. + Task GetAuthenticationResultForUserAsync( + IEnumerable scopes, + string? authenticationScheme = null, + ClaimsPrincipal? user = null, + TokenAcquisitionOptions? tokenAcquisitionOptions = null); + + /// + /// Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + /// using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + /// + /// The scope requested to access a protected API. For this flow (client credentials), the scope + /// should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + /// Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + /// in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + /// several calls to get tokens for other resources). + /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. + /// An authentication result for the app itself, based on its scopes. + Task GetAuthenticationResultForAppAsync( + string scope, + string? authenticationScheme = null, + TokenAcquisitionOptions? tokenAcquisitionOptions = null); + } +} diff --git a/src/Microsoft.Identity.Web/ITokenAcquisition.cs b/src/Microsoft.Identity.Web/ITokenAcquisition.cs index 052339bef..ccb9162f8 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/ITokenAcquisition.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; +#if !NET472 && !NET462 using Microsoft.AspNetCore.Http; +#endif using Microsoft.Identity.Client; namespace Microsoft.Identity.Web @@ -15,6 +17,7 @@ namespace Microsoft.Identity.Web /// public interface ITokenAcquisition { +#if !NET472 && !NET462 /// /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token /// for a downstream API on behalf of the user account for which the claims are provided in the @@ -44,11 +47,11 @@ Task GetAccessTokenForUserAsync( user, tokenAcquisitionOptions); } +#endif /// /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - /// for a downstream API on behalf of the user account for which the claims are provided in the - /// member of the controller's parameter. + /// for a downstream API on behalf of the user account for which the claims are provided in the current user /// /// Scopes to request for the downstream API to call. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme @@ -69,10 +72,10 @@ Task GetAccessTokenForUserAsync( ClaimsPrincipal? user = null, TokenAcquisitionOptions? tokenAcquisitionOptions = null); +#if !NET472 && !NET462 /// /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - /// for a downstream API on behalf of the user account for which the claims are provided in the - /// member of the controller's parameter. + /// for a downstream API on behalf of the user account for which the claims are provided in the current user /// /// Scopes to request for the downstream API to call. /// Enables to override the tenant/account for the same identity. This is useful in the @@ -98,11 +101,11 @@ Task GetAuthenticationResultForUserAsync( user, tokenAcquisitionOptions); } +#endif /// /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - /// for a downstream API on behalf of the user account for which the claims are provided in the - /// member of the controller's parameter. + /// for a downstream API on behalf of the user account for which the claims are provided in the current user /// /// Scopes to request for the downstream API to call. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme @@ -123,6 +126,7 @@ Task GetAuthenticationResultForUserAsync( ClaimsPrincipal? user = null, TokenAcquisitionOptions? tokenAcquisitionOptions = null); +#if !NET472 && !NET462 /// /// Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) /// using the client credentials flow. See https://aka.ms/msal-net-client-credentials. @@ -147,6 +151,7 @@ Task GetAccessTokenForAppAsync( tenant, tokenAcquisitionOptions); } +#endif /// /// Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -169,6 +174,7 @@ Task GetAccessTokenForAppAsync( string? tenant = null, TokenAcquisitionOptions? tokenAcquisitionOptions = null); +#if !NET472 && !NET462 /// /// Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) /// using the client credentials flow. See https://aka.ms/msal-net-client-credentials. @@ -193,6 +199,7 @@ Task GetAuthenticationResultForAppAsync( tenant, tokenAcquisitionOptions); } +#endif /// /// Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -215,6 +222,7 @@ Task GetAuthenticationResultForAppAsync( string? tenant = null, TokenAcquisitionOptions? tokenAcquisitionOptions = null); +#if !NET472 && !NET462 /// /// Used in web APIs (which therefore cannot have an interaction with the user). /// Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that @@ -250,6 +258,7 @@ void ReplyForbiddenWithWwwAuthenticateHeader( MsalUiRequiredException msalServiceException, string? authenticationScheme, HttpResponse? httpResponse = null); +#endif /// /// Get the effective authentication scheme based on the context. @@ -260,6 +269,7 @@ void ReplyForbiddenWithWwwAuthenticateHeader( /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. string GetEffectiveAuthenticationScheme(string? authenticationScheme); +#if !NET472 && !NET462 /// /// Used in web APIs (which therefore cannot have an interaction with the user). /// Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that @@ -274,5 +284,6 @@ Task ReplyForbiddenWithWwwAuthenticateHeaderAsync( IEnumerable scopes, MsalUiRequiredException msalServiceException, HttpResponse? httpResponse = null); +#endif } } diff --git a/src/Microsoft.Identity.Web/ITokenAcquisitionHost.cs b/src/Microsoft.Identity.Web/ITokenAcquisitionHost.cs new file mode 100644 index 000000000..f073b273f --- /dev/null +++ b/src/Microsoft.Identity.Web/ITokenAcquisitionHost.cs @@ -0,0 +1,23 @@ +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.Identity.Web +{ + internal interface ITokenAcquisitionHost + { + MergedOptions GetOptions(string authenticationScheme); + + // TODO: discuss if we don't want to merge with GetOptions + void ProcessOptionsForAnonymousControllers(string authenticationScheme, MergedOptions mergedOptions); + + void SetSession(string key, string value); + string GetEffectiveAuthenticationScheme(string? authenticationScheme); + string? GetCurrentRedirectUri(MergedOptions mergedOptions); + SecurityToken? GetTokenUsedToCallWebAPI(); + Task GetAuthenticatedUserAsync(ClaimsPrincipal? user); + ClaimsPrincipal? GetUserFromRequest(); + void SetHttpResponse(HttpStatusCode statusCode, string wwwAuthenticate); + } +} diff --git a/src/Microsoft.Identity.Web/ITokenAcquisitionInternal.cs b/src/Microsoft.Identity.Web/ITokenAcquisitionInternal.cs index 97cc34199..bea37953e 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquisitionInternal.cs +++ b/src/Microsoft.Identity.Web/ITokenAcquisitionInternal.cs @@ -2,8 +2,11 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Security.Claims; using System.Threading.Tasks; +#if !NET472 && !NET462 using Microsoft.AspNetCore.Authentication.OpenIdConnect; +#endif namespace Microsoft.Identity.Web { @@ -12,6 +15,7 @@ namespace Microsoft.Identity.Web /// internal interface ITokenAcquisitionInternal : ITokenAcquisition { +#if !NET472 && !NET462 /// /// In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user /// signed-in and consented) @@ -46,15 +50,36 @@ Task AddAccountToCacheFromAuthorizationCodeAsync( AuthorizationCodeReceivedContext context, IEnumerable scopes, string authenticationScheme = OpenIdConnectDefaults.AuthenticationScheme); +#else + /// + /// In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user + /// signed-in and consented) + /// the token redeemed from the "/> is added to the cache, so that it can then be used to acquire another token on-behalf-of the + /// same user in order to call to downstream APIs. + /// + /// Scopes to request. Can be empty + /// Authorization code + /// Authentication scheme to use (config section) + /// Client Info obtained with the code + /// PKCE code verifier + /// User flow in the case of B2C + /// The ID Token. + Task AddAccountToCacheFromAuthorizationCodeAsync( + IEnumerable scopes, + string authCode, + string authenticationScheme, + string? clientInfo, + string? codeVerifier, + string? userFlow); +#endif /// /// Removes the account associated with context.HttpContext.User from the MSAL.NET cache. /// - /// RedirectContext passed-in to a - /// OpenID Connect event. + /// Signed in user /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. /// A that represents a completed remove from cache operation. - Task RemoveAccountAsync(RedirectContext context, string? authenticationScheme = null); + Task RemoveAccountAsync(ClaimsPrincipal user, string? authenticationScheme = null); } } diff --git a/src/Microsoft.Identity.Web/MergedOptions.cs b/src/Microsoft.Identity.Web/MergedOptions.cs index 1500ec398..d74a359a5 100644 --- a/src/Microsoft.Identity.Web/MergedOptions.cs +++ b/src/Microsoft.Identity.Web/MergedOptions.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; +#if !NET472 && !NET462 using Microsoft.AspNetCore.Authentication.JwtBearer; +#endif using Microsoft.Identity.Client; namespace Microsoft.Identity.Web @@ -48,19 +50,15 @@ public ConfidentialClientApplicationOptions ConfidentialClientApplicationOptions internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftIdentityOptions microsoftIdentityOptions, MergedOptions mergedOptions) { - mergedOptions.ResetPasswordPath = microsoftIdentityOptions.ResetPasswordPath; - mergedOptions.ErrorPath = microsoftIdentityOptions.ErrorPath; - mergedOptions.AccessDeniedPath = microsoftIdentityOptions.AccessDeniedPath; - mergedOptions.AllowWebApiToBeAuthorizedByACL = microsoftIdentityOptions.AllowWebApiToBeAuthorizedByACL; - mergedOptions.AuthenticationMethod = microsoftIdentityOptions.AuthenticationMethod; - if (string.IsNullOrEmpty(mergedOptions.Authority) && !string.IsNullOrEmpty(microsoftIdentityOptions.Authority)) - { - mergedOptions.Authority = microsoftIdentityOptions.Authority; - } #if DOTNET_50_AND_ABOVE - mergedOptions.AutomaticRefreshInterval = microsoftIdentityOptions.AutomaticRefreshInterval; + mergedOptions.MapInboundClaims = microsoftIdentityOptions.MapInboundClaims; #endif + +#if !NET472 && !NET462 +// ASP.NET Core specific + mergedOptions.AccessDeniedPath = microsoftIdentityOptions.AccessDeniedPath; + mergedOptions.AuthenticationMethod = microsoftIdentityOptions.AuthenticationMethod; mergedOptions.Backchannel ??= microsoftIdentityOptions.Backchannel; mergedOptions.BackchannelHttpHandler ??= microsoftIdentityOptions.BackchannelHttpHandler; mergedOptions.BackchannelTimeout = microsoftIdentityOptions.BackchannelTimeout; @@ -69,34 +67,12 @@ internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftId { mergedOptions.ClaimsIssuer = microsoftIdentityOptions.ClaimsIssuer; } - - mergedOptions.ClientCertificates ??= microsoftIdentityOptions.ClientCertificates; - if (string.IsNullOrEmpty(mergedOptions.ClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientId)) - { - mergedOptions.ClientId = microsoftIdentityOptions.ClientId; - } - - if (string.IsNullOrEmpty(mergedOptions.ClientSecret) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientSecret)) - { - mergedOptions.ClientSecret = microsoftIdentityOptions.ClientSecret; - } - mergedOptions.Configuration ??= microsoftIdentityOptions.Configuration; mergedOptions.ConfigurationManager ??= microsoftIdentityOptions.ConfigurationManager; mergedOptions.CorrelationCookie = microsoftIdentityOptions.CorrelationCookie; mergedOptions.DataProtectionProvider ??= microsoftIdentityOptions.DataProtectionProvider; mergedOptions.DisableTelemetry = microsoftIdentityOptions.DisableTelemetry; - if (string.IsNullOrEmpty(mergedOptions.Domain) && !string.IsNullOrEmpty(microsoftIdentityOptions.Domain)) - { - mergedOptions.Domain = microsoftIdentityOptions.Domain; - } - - if (string.IsNullOrEmpty(mergedOptions.EditProfilePolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.EditProfilePolicyId)) - { - mergedOptions.EditProfilePolicyId = microsoftIdentityOptions.EditProfilePolicyId; - } - mergedOptions.Events.OnAccessDenied += microsoftIdentityOptions.Events.OnAccessDenied; mergedOptions.Events.OnAuthenticationFailed += microsoftIdentityOptions.Events.OnAuthenticationFailed; mergedOptions.Events.OnAuthorizationCodeReceived += microsoftIdentityOptions.Events.OnAuthorizationCodeReceived; @@ -149,12 +125,6 @@ internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftId mergedOptions.Instance = microsoftIdentityOptions.Instance; } - mergedOptions.LegacyCacheCompatibilityEnabled = microsoftIdentityOptions.LegacyCacheCompatibilityEnabled; - -#if DOTNET_50_AND_ABOVE - mergedOptions.MapInboundClaims = microsoftIdentityOptions.MapInboundClaims; -#endif - mergedOptions.MaxAge = microsoftIdentityOptions.MaxAge; if (string.IsNullOrEmpty(mergedOptions.MetadataAddress) && !string.IsNullOrEmpty(microsoftIdentityOptions.MetadataAddress)) { @@ -219,32 +189,16 @@ internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftId mergedOptions.SignOutScheme = microsoftIdentityOptions.SignOutScheme; } - if (string.IsNullOrEmpty(mergedOptions.SignUpSignInPolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignUpSignInPolicyId)) - { - mergedOptions.SignUpSignInPolicyId = microsoftIdentityOptions.SignUpSignInPolicyId; - } - mergedOptions.SkipUnrecognizedRequests = microsoftIdentityOptions.SkipUnrecognizedRequests; mergedOptions.StateDataFormat ??= microsoftIdentityOptions.StateDataFormat; mergedOptions.StringDataFormat ??= microsoftIdentityOptions.StringDataFormat; - if (string.IsNullOrEmpty(mergedOptions.TenantId) && !string.IsNullOrEmpty(microsoftIdentityOptions.TenantId)) - { - mergedOptions.TenantId = microsoftIdentityOptions.TenantId; - } - mergedOptions.TokenDecryptionCertificates ??= microsoftIdentityOptions.TokenDecryptionCertificates; mergedOptions.TokenValidationParameters = microsoftIdentityOptions.TokenValidationParameters.Clone(); mergedOptions.UsePkce = microsoftIdentityOptions.UsePkce; - if (string.IsNullOrEmpty(mergedOptions.UserAssignedManagedIdentityClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.UserAssignedManagedIdentityClientId)) - { - mergedOptions.UserAssignedManagedIdentityClientId = microsoftIdentityOptions.UserAssignedManagedIdentityClientId; - } mergedOptions.UseTokenLifetime = microsoftIdentityOptions.UseTokenLifetime; - mergedOptions._confidentialClientApplicationOptions = null; mergedOptions.Scope.Clear(); - foreach (var scope in microsoftIdentityOptions.Scope) { if (!string.IsNullOrWhiteSpace(scope) && !mergedOptions.Scope.Any(s => string.Equals(s, scope, StringComparison.OrdinalIgnoreCase))) @@ -252,6 +206,60 @@ internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftId mergedOptions.Scope.Add(scope); } } +#if DOTNET_50_AND_ABOVE + mergedOptions.AutomaticRefreshInterval = microsoftIdentityOptions.AutomaticRefreshInterval; +#endif +#endif + // Non ASP.NET Core specific + mergedOptions.ResetPasswordPath = microsoftIdentityOptions.ResetPasswordPath; + mergedOptions.ErrorPath = microsoftIdentityOptions.ErrorPath; + mergedOptions.AllowWebApiToBeAuthorizedByACL = microsoftIdentityOptions.AllowWebApiToBeAuthorizedByACL; + if (string.IsNullOrEmpty(mergedOptions.Authority) && !string.IsNullOrEmpty(microsoftIdentityOptions.Authority)) + { + mergedOptions.Authority = microsoftIdentityOptions.Authority; + } + + mergedOptions.ClientCertificates ??= microsoftIdentityOptions.ClientCertificates; + if (string.IsNullOrEmpty(mergedOptions.ClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientId)) + { + mergedOptions.ClientId = microsoftIdentityOptions.ClientId; + } + + if (string.IsNullOrEmpty(mergedOptions.ClientSecret) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientSecret)) + { + mergedOptions.ClientSecret = microsoftIdentityOptions.ClientSecret; + } + + if (string.IsNullOrEmpty(mergedOptions.Domain) && !string.IsNullOrEmpty(microsoftIdentityOptions.Domain)) + { + mergedOptions.Domain = microsoftIdentityOptions.Domain; + } + + if (string.IsNullOrEmpty(mergedOptions.EditProfilePolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.EditProfilePolicyId)) + { + mergedOptions.EditProfilePolicyId = microsoftIdentityOptions.EditProfilePolicyId; + } + + mergedOptions.LegacyCacheCompatibilityEnabled = microsoftIdentityOptions.LegacyCacheCompatibilityEnabled; + + if (string.IsNullOrEmpty(mergedOptions.SignUpSignInPolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignUpSignInPolicyId)) + { + mergedOptions.SignUpSignInPolicyId = microsoftIdentityOptions.SignUpSignInPolicyId; + } + + if (string.IsNullOrEmpty(mergedOptions.TenantId) && !string.IsNullOrEmpty(microsoftIdentityOptions.TenantId)) + { + mergedOptions.TenantId = microsoftIdentityOptions.TenantId; + } + + mergedOptions.TokenDecryptionCertificates ??= microsoftIdentityOptions.TokenDecryptionCertificates; + + if (string.IsNullOrEmpty(mergedOptions.UserAssignedManagedIdentityClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.UserAssignedManagedIdentityClientId)) + { + mergedOptions.UserAssignedManagedIdentityClientId = microsoftIdentityOptions.UserAssignedManagedIdentityClientId; + } + + mergedOptions._confidentialClientApplicationOptions = null; } internal static void UpdateMergedOptionsFromConfidentialClientApplicationOptions(ConfidentialClientApplicationOptions confidentialClientApplicationOptions, MergedOptions mergedOptions) @@ -359,6 +367,7 @@ internal static void UpdateConfidentialClientApplicationOptionsFromMergedOptions } } +#if !NET472 && !NET462 internal static void UpdateMergedOptionsFromJwtBearerOptions(JwtBearerOptions jwtBearerOptions, MergedOptions mergedOptions) { if (string.IsNullOrEmpty(mergedOptions.Authority) && !string.IsNullOrEmpty(jwtBearerOptions.Authority)) @@ -366,12 +375,17 @@ internal static void UpdateMergedOptionsFromJwtBearerOptions(JwtBearerOptions jw mergedOptions.Authority = jwtBearerOptions.Authority; } } +#endif public void PrepareAuthorityInstanceForMsal() { if (IsB2C && Instance.EndsWith("/tfp/", StringComparison.OrdinalIgnoreCase)) { +#if !NET472 && !NET462 Instance = Instance.Replace("/tfp/", string.Empty, StringComparison.OrdinalIgnoreCase).TrimEnd('/') + "/"; +#else + Instance = Instance.Replace("/tfp/", string.Empty).TrimEnd('/') + "/"; +#endif } else { diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj index b46aa7a78..7afb1d134 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj @@ -61,7 +61,12 @@ enable true - + + + $(DefineConstants);DOTNET_50_AND_ABOVE @@ -106,12 +111,28 @@ + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 41bbaaa07..7b7f5713d 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -1203,6 +1203,41 @@ Issuer associated with the OIDC endpoint. + + + Interface for the token acquisition service (encapsulating MSAL.NET). + + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An to call on behalf of the user, the downstream API characterized by its scopes. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + Interface for the token acquisition service (encapsulating MSAL.NET). @@ -1227,8 +1262,7 @@ Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. + for a downstream API on behalf of the user account for which the claims are provided in the current user Scopes to request for the downstream API to call. Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme @@ -1245,8 +1279,7 @@ Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. + for a downstream API on behalf of the user account for which the claims are provided in the current user Scopes to request for the downstream API to call. Enables to override the tenant/account for the same identity. This is useful in the @@ -1261,8 +1294,7 @@ Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. + for a downstream API on behalf of the user account for which the claims are provided in the current user Scopes to request for the downstream API to call. Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme @@ -1419,12 +1451,11 @@ - + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. - RedirectContext passed-in to a - OpenID Connect event. + Signed in user Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. A that represents a completed remove from cache operation. @@ -2184,33 +2215,40 @@ LoggingMessage class for TokenAcquisition. - - - Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. - - - + Constructor of the TokenAcquisition service. This requires the Azure AD Options to configure the confidential client application and a token cache provider. This constructor is called by ASP.NET Core dependency injection. The App token cache provider. - Access to the HttpContext of the request. - Configuration options. + Host of the token acquisition service. HTTP client factory. Logger. Service provider. - + - Scopes which are already requested by MSAL.NET. They should not be re-requested;. + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + Scopes to consent to. + The that triggered the challenge. + The to update. + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - + - Meta-tenant identifiers which are not allowed in client credentials. + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + Scopes to consent to. + The that triggered the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. @@ -2245,13 +2283,40 @@ + + + Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. + + + + + Scopes which are already requested by MSAL.NET. They should not be re-requested;. + + + + + Meta-tenant identifiers which are not allowed in client credentials. + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + The App token cache provider. + Host of the token acquisition. + HTTP client factory. + Logger. + Service provider. + Typically used from a web app or web API controller, this method retrieves an access token for a downstream API using; 1) the token cache (for web apps and web APIs) if a token exists in the cache 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from claims provided in the + in web APIs, for the user account that is ascertained from claims provided in the current claims principal. instance of the current HttpContext. Scopes to request for the downstream API to call. @@ -2309,7 +2374,7 @@ for a downstream API using; 1) the token cache (for web apps and web APIs) if a token exists in the cache 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from the claims provided in the + in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. instance of the current HttpContext. Scopes to request for the downstream API to call. @@ -2329,42 +2394,15 @@ you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - - - Used in web APIs (no user interaction). - Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - the client, in turn, can trigger a user interaction so that the user consents to more scopes. - - Scopes to consent to. - The that triggered the challenge. - The to update. - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - - - - Used in web APIs (no user interaction). - Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - the client, in turn, can trigger a user interaction so that the user consents to more scopes. - - Scopes to consent to. - The that triggered the challenge. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The to update. - - + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. - RedirectContext passed-in to a - OpenID Connect event. + User Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. A that represents a completed account removal operation. - - - Creates an MSAL confidential client application. @@ -2418,11 +2456,43 @@ cacheRefreshReason. Exception from MSAL.NET. + + + Implementation of ITokenAcquisitionHost in the case of ASP.NET Core + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + Access to the HttpContext of the request. + Configuration options. + Logger. + Service provider. + + + + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps + in the cases where a given account is a guest in other tenants, and you want to acquire tokens + for a specific tenant. + + Can be the tenant ID or domain name. + + + + Uses a particular user flow (in the case of AzureAD B2C) + + Sets the correlation id to be used in the authentication request diff --git a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs index 282ed4f82..187e1b0e0 100644 --- a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs +++ b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs @@ -4,11 +4,53 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; +#if !NET472 && !NET462 using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Http; +#endif + +#if NET472 || NET462 +using PathString = System.String; +#endif namespace Microsoft.Identity.Web { +#if NET472 || NET462 + /// + /// OpenIdConnect options + /// + public class OpenIdConnectOptions + { + /// + /// Gets or sets the Authority to use when making OpenIdConnect calls. + /// + public string? Authority + { + get; + set; + } + + /// + /// Client secret used to authenticate a confidential client app to AAD + /// (alternatively use client certificates) + /// + public string? ClientSecret { get; set; } + + /// + /// Gets or sets the 'client_id' (application ID) as appears in the + /// application registration. + /// + public string? ClientId + { + get; + set; + } + } +#endif + /// /// Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. /// @@ -146,13 +188,13 @@ internal bool HasClientCredentials /// Defaults to /MicrosoftIdentity/Account/ResetPassword, /// which is the value used by Microsoft.Identity.Web.UI. /// - public PathString ResetPasswordPath { get; set; } = new PathString("/MicrosoftIdentity/Account/ResetPassword"); + public PathString ResetPasswordPath { get; set; } = "/MicrosoftIdentity/Account/ResetPassword"; /// /// Sets the Error route path. /// Defaults to the value /MicrosoftIdentity/Account/Error, /// which is the value used by Microsoft.Identity.Web.UI. /// - public PathString ErrorPath { get; set; } = new PathString("/MicrosoftIdentity/Account/Error"); + public PathString ErrorPath { get; set; } = "/MicrosoftIdentity/Account/Error"; } } diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index ba933abad..e39b20e75 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -61,11 +61,13 @@ public static IServiceCollection AddTokenAcquisition( if (isTokenAcquisitionSingleton) { services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(s => (ITokenAcquisitionInternal)s.GetRequiredService()); } else { services.AddScoped(); + services.AddScoped(); services.AddScoped(s => (ITokenAcquisitionInternal)s.GetRequiredService()); } diff --git a/src/Microsoft.Identity.Web/TokenAcquisition-AspnetCore.cs b/src/Microsoft.Identity.Web/TokenAcquisition-AspnetCore.cs new file mode 100644 index 000000000..4dfe9f3d1 --- /dev/null +++ b/src/Microsoft.Identity.Web/TokenAcquisition-AspnetCore.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; +using Microsoft.Identity.Client; +using Microsoft.Identity.Web.TokenCacheProviders; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.Identity.Web +{ + partial class TokenAcquisition + { + + /// + /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to + /// configure the confidential client application and a token cache provider. + /// This constructor is called by ASP.NET Core dependency injection. + /// + /// The App token cache provider. + /// Host of the token acquisition service. + /// HTTP client factory. + /// Logger. + /// Service provider. + public TokenAcquisition( + IMsalTokenCacheProvider tokenCacheProvider, + IHttpClientFactory httpClientFactory, + ILogger logger, + ITokenAcquisitionHost tokenAcquisitionHost, + IServiceProvider serviceProvider) + { + _tokenCacheProvider = tokenCacheProvider; + _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); + _logger = logger; + _tokenAcquisitionHost = tokenAcquisitionHost; + _serviceProvider = serviceProvider; + } + + + /// + /// Used in web APIs (no user interaction). + /// Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + /// the client, in turn, can trigger a user interaction so that the user consents to more scopes. + /// + /// Scopes to consent to. + /// The that triggered the challenge. + /// The to update. + /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + public Task ReplyForbiddenWithWwwAuthenticateHeaderAsync( + IEnumerable scopes, + MsalUiRequiredException msalServiceException, + HttpResponse? httpResponse = null) + { + ReplyForbiddenWithWwwAuthenticateHeader(scopes, msalServiceException, null, httpResponse); + return Task.CompletedTask; + } + + /// + /// Used in web APIs (no user interaction). + /// Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + /// the client, in turn, can trigger a user interaction so that the user consents to more scopes. + /// + /// Scopes to consent to. + /// The that triggered the challenge. + /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + /// The to update. + public void ReplyForbiddenWithWwwAuthenticateHeader( + IEnumerable scopes, + MsalUiRequiredException msalServiceException, + string? authenticationScheme = JwtBearerDefaults.AuthenticationScheme, + HttpResponse? httpResponse = null) + { + // A user interaction is required, but we are in a web API, and therefore, we need to report back to the client through a 'WWW-Authenticate' header https://tools.ietf.org/html/rfc6750#section-3.1 + string proposedAction = Constants.Consent; + if (msalServiceException.ErrorCode == MsalError.InvalidGrantError && AcceptedTokenVersionMismatch(msalServiceException)) + { + throw msalServiceException; + } + + authenticationScheme = _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); + MergedOptions mergedOptions = _tokenAcquisitionHost.GetOptions(authenticationScheme); + + var application = GetOrBuildConfidentialClientApplication(mergedOptions); + + string consentUrl = $"{application.Authority}/oauth2/v2.0/authorize?client_id={mergedOptions.ClientId}" + + $"&response_type=code&redirect_uri={application.AppConfig.RedirectUri}" + + $"&response_mode=query&scope=offline_access%20{string.Join("%20", scopes)}"; + + IDictionary parameters = new Dictionary() + { + { Constants.ConsentUrl, consentUrl }, + { Constants.Claims, msalServiceException.Claims }, + { Constants.Scopes, string.Join(",", scopes) }, + { Constants.ProposedAction, proposedAction }, + }; + + string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\"")); + + // TODO: change the mechanism to Challenge. + _tokenAcquisitionHost.SetHttpResponse(HttpStatusCode.Forbidden, new StringValues($"{Constants.Bearer} {parameterString}")); + } + + /// + /// This handler is executed after the authorization code is received (once the user signs-in and consents) during the + /// authorization code flow in a web app. + /// It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. + /// The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the + /// on-behalf-of flow for the signed-in user's account, + /// in order to call to downstream APIs. + /// + /// The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + /// scopes to request access to. + /// Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). + /// + /// From the configuration of the Authentication of the ASP.NET Core web API: + /// OpenIdConnectOptions options; + /// + /// Subscribe to the authorization code received event: + /// + /// options.Events = new OpenIdConnectEvents(); + /// options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + /// } + /// + /// + /// And then in the OnAuthorizationCodeRecieved method, call : + /// + /// private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + /// { + /// var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + /// await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + /// } + /// + /// + public async Task AddAccountToCacheFromAuthorizationCodeAsync( + AuthorizationCodeReceivedContext context, + IEnumerable scopes, + string authenticationScheme /*= OpenIdConnectDefaults.AuthenticationScheme*/) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (scopes == null) + { + throw new ArgumentNullException(nameof(scopes)); + } + + string? clientInfo = context!.ProtocolMessage?.GetParameter(ClaimConstants.ClientInfo); + context.TokenEndpointRequest.Parameters.TryGetValue(OAuthConstants.CodeVerifierKey, out string? codeVerifier); + string authCode = context!.ProtocolMessage!.Code; + string? userFlow = context.Principal?.GetUserFlowId(); + + string idToken = await AddAccountToCacheFromAuthorizationCodeAsync(scopes, authCode, authenticationScheme, clientInfo, codeVerifier, userFlow).ConfigureAwait(false); + context.HandleCodeRedemption(null, idToken); + } + } +} diff --git a/src/Microsoft.Identity.Web/TokenAcquisition.cs b/src/Microsoft.Identity.Web/TokenAcquisition.cs index ac38e69c4..dbcdd143c 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisition.cs @@ -6,38 +6,34 @@ using System.Globalization; using System.IdentityModel.Tokens.Jwt; using System.Linq; -using System.Net; using System.Net.Http; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; using Microsoft.Identity.Client; using Microsoft.Identity.Web.TokenCacheProviders; using Microsoft.Identity.Web.TokenCacheProviders.InMemory; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; -using Microsoft.Net.Http.Headers; namespace Microsoft.Identity.Web { /// /// Token acquisition service. /// - internal partial class TokenAcquisition : ITokenAcquisitionInternal + internal partial class TokenAcquisition : ITokenAcquisition, ITokenAcquisitionInternal { - private readonly IOptionsMonitor _mergedOptionsMonitor; +#if NET472 || NET462 + class OAuthConstants + { + public static readonly string CodeVerifierKey = "code_verifier"; + } +#endif + private readonly IMsalTokenCacheProvider _tokenCacheProvider; private readonly object _applicationSyncObj = new object(); @@ -47,53 +43,10 @@ internal partial class TokenAcquisition : ITokenAcquisitionInternal /// private IConfidentialClientApplication? _application; private bool _retryClientCertificate; - private readonly IHttpContextAccessor _httpContextAccessor; - private HttpContext? CurrentHttpContext => _httpContextAccessor.HttpContext; private readonly IMsalHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; - - /// - /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to - /// configure the confidential client application and a token cache provider. - /// This constructor is called by ASP.NET Core dependency injection. - /// - /// The App token cache provider. - /// Access to the HttpContext of the request. - /// Configuration options. - /// HTTP client factory. - /// Logger. - /// Service provider. - public TokenAcquisition( - IMsalTokenCacheProvider tokenCacheProvider, - IHttpContextAccessor httpContextAccessor, - IOptionsMonitor mergedOptionsMonitor, - IHttpClientFactory httpClientFactory, - ILogger logger, - IServiceProvider serviceProvider) - { - _httpContextAccessor = httpContextAccessor; - _mergedOptionsMonitor = mergedOptionsMonitor; - _tokenCacheProvider = tokenCacheProvider; - _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); - _logger = logger; - _serviceProvider = serviceProvider; - } - - internal MergedOptions GetOptions(string authenticationScheme) - { - var mergedOptions = _mergedOptionsMonitor.Get(authenticationScheme); - if (!mergedOptions.MergedWithCca) - { - var ccaOptionsMonitor = _serviceProvider.GetService>(); - ccaOptionsMonitor?.Get(authenticationScheme); - var bearerMonitor = _serviceProvider.GetService>(); // supports event handlers - bearerMonitor?.Get(authenticationScheme); - } - - DefaultCertificateLoader.UserAssignedManagedIdentityClientId = mergedOptions.UserAssignedManagedIdentityClientId; - return mergedOptions; - } + private readonly ITokenAcquisitionHost _tokenAcquisitionHost; /// /// Scopes which are already requested by MSAL.NET. They should not be re-requested;. @@ -117,68 +70,46 @@ internal MergedOptions GetOptions(string authenticationScheme) StringComparer.OrdinalIgnoreCase); /// - /// This handler is executed after the authorization code is received (once the user signs-in and consents) during the - /// authorization code flow in a web app. - /// It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. - /// The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the - /// on-behalf-of flow for the signed-in user's account, - /// in order to call to downstream APIs. + /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to + /// configure the confidential client application and a token cache provider. + /// This constructor is called by ASP.NET Core dependency injection. /// - /// The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. - /// scopes to request access to. - /// Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). - /// - /// From the configuration of the Authentication of the ASP.NET Core web API: - /// OpenIdConnectOptions options; - /// - /// Subscribe to the authorization code received event: - /// - /// options.Events = new OpenIdConnectEvents(); - /// options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; - /// } - /// - /// - /// And then in the OnAuthorizationCodeRecieved method, call : - /// - /// private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) - /// { - /// var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); - /// await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); - /// } - /// - /// - public async Task AddAccountToCacheFromAuthorizationCodeAsync( - AuthorizationCodeReceivedContext context, - IEnumerable scopes, - string authenticationScheme /*= OpenIdConnectDefaults.AuthenticationScheme*/) + /// The App token cache provider. + /// Host of the token acquisition. + /// HTTP client factory. + /// Logger. + /// Service provider. + public TokenAcquisition( + IMsalTokenCacheProvider tokenCacheProvider, + ITokenAcquisitionHost tokenAcquisitionHost, + IHttpClientFactory httpClientFactory, + ILogger logger, + IServiceProvider serviceProvider) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (scopes == null) - { - throw new ArgumentNullException(nameof(scopes)); - } + _tokenCacheProvider = tokenCacheProvider; + _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); + _logger = logger; + _serviceProvider = serviceProvider; + _tokenAcquisitionHost = tokenAcquisitionHost; + } - authenticationScheme = GetEffectiveAuthenticationScheme(authenticationScheme); - MergedOptions mergedOptions = GetOptions(authenticationScheme); + public async Task AddAccountToCacheFromAuthorizationCodeAsync(IEnumerable scopes, string authCode, string authenticationScheme, string? clientInfo, string? codeVerifier, string? userFlow) + { + authenticationScheme = _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); + MergedOptions mergedOptions = _tokenAcquisitionHost.GetOptions(authenticationScheme); try { var application = GetOrBuildConfidentialClientApplication(mergedOptions); - context.TokenEndpointRequest.Parameters.TryGetValue(OAuthConstants.CodeVerifierKey, out string? codeVerifier); // Do not share the access token with ASP.NET Core otherwise ASP.NET will cache it and will not send the OAuth 2.0 request in // case a further call to AcquireTokenByAuthorizationCodeAsync in the future is required for incremental consent (getting a code requesting more scopes) // Share the ID token though - string? clientInfo = context!.ProtocolMessage?.GetParameter(ClaimConstants.ClientInfo); string? backUpAuthRoutingHint = string.Empty; if (!string.IsNullOrEmpty(clientInfo)) { - ClientInfo? clientInfoFromAuthorize = ClientInfo.CreateFromJson(clientInfo); + ClientInfo? clientInfoFromAuthorize = ClientInfo.CreateFromJson(clientInfo!); if (clientInfoFromAuthorize != null && clientInfoFromAuthorize.UniqueTenantIdentifier != null && clientInfoFromAuthorize.UniqueObjectIdentifier != null) { backUpAuthRoutingHint = $"oid:{clientInfoFromAuthorize.UniqueObjectIdentifier}@{clientInfoFromAuthorize.UniqueTenantIdentifier}"; @@ -186,7 +117,7 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( } var builder = application - .AcquireTokenByAuthorizationCode(scopes.Except(_scopesRequestedByMsal), context!.ProtocolMessage!.Code) + .AcquireTokenByAuthorizationCode(scopes.Except(_scopesRequestedByMsal), authCode) .WithSendX5C(mergedOptions.SendX5C) .WithPkceCodeVerifier(codeVerifier) .WithCcsRoutingHint(backUpAuthRoutingHint) @@ -194,7 +125,7 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( if (mergedOptions.IsB2C) { - string? userFlow = context.Principal?.GetUserFlowId(); + var authority = $"{mergedOptions.Instance}{ClaimConstants.Tfp}/{mergedOptions.Domain}/{userFlow ?? mergedOptions.DefaultUserFlow}"; builder.WithB2CAuthority(authority); } @@ -202,11 +133,12 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( var result = await builder.ExecuteAsync() .ConfigureAwait(false); - context.HandleCodeRedemption(null, result.IdToken); if (!string.IsNullOrEmpty(result.SpaAuthCode)) { - CurrentHttpContext?.Session.SetString(Constants.SpaAuthCode, result.SpaAuthCode); + _tokenAcquisitionHost.SetSession(Constants.SpaAuthCode, result.SpaAuthCode); } + + return result.IdToken; } catch (MsalServiceException exMsal) when (IsInvalidClientCertificateError(exMsal)) { @@ -215,7 +147,7 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( // Retry _retryClientCertificate = true; - await AddAccountToCacheFromAuthorizationCodeAsync(context, scopes, authenticationScheme).ConfigureAwait(false); + await AddAccountToCacheFromAuthorizationCodeAsync(scopes, authCode, authenticationScheme, clientInfo, codeVerifier, userFlow).ConfigureAwait(false); } catch (MsalException ex) { @@ -226,6 +158,8 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( { _retryClientCertificate = false; } + + return authenticationScheme; } /// @@ -233,7 +167,7 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync( /// for a downstream API using; /// 1) the token cache (for web apps and web APIs) if a token exists in the cache /// 2) or the on-behalf-of flow - /// in web APIs, for the user account that is ascertained from claims provided in the + /// in web APIs, for the user account that is ascertained from claims provided in the current claims principal. /// instance of the current HttpContext. /// /// Scopes to request for the downstream API to call. @@ -265,8 +199,8 @@ public async Task GetAuthenticationResultForUserAsync( throw new ArgumentNullException(nameof(scopes)); } - authenticationScheme = GetEffectiveAuthenticationScheme(authenticationScheme); - MergedOptions mergedOptions = GetOptions(authenticationScheme); + authenticationScheme = _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); + MergedOptions mergedOptions = _tokenAcquisitionHost.GetOptions(authenticationScheme); if (string.IsNullOrEmpty(mergedOptions.Instance)) { @@ -274,7 +208,7 @@ public async Task GetAuthenticationResultForUserAsync( mergedOptionsMonitor.Get(authenticationScheme); } - user = await GetAuthenticatedUserAsync(user).ConfigureAwait(false); + user = await _tokenAcquisitionHost.GetAuthenticatedUserAsync(user).ConfigureAwait(false); var application = GetOrBuildConfidentialClientApplication(mergedOptions); @@ -378,29 +312,19 @@ public Task GetAuthenticationResultForAppAsync( throw new ArgumentException(IDWebErrorMessage.ClientCredentialScopeParameterShouldEndInDotDefault, nameof(scope)); } - authenticationScheme = GetEffectiveAuthenticationScheme(authenticationScheme); + authenticationScheme = _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); - MergedOptions mergedOptions = GetOptions(authenticationScheme); + MergedOptions mergedOptions = _tokenAcquisitionHost.GetOptions(authenticationScheme); - // Case of an anonymous controller, no [Authorize] attribute will trigger the merge options - if (string.IsNullOrEmpty(mergedOptions.Instance)) - { - var mergedOptionsMonitor = _serviceProvider.GetService>(); - mergedOptionsMonitor?.Get(authenticationScheme); - } - // Case of an anonymous controller called from a web app - if (string.IsNullOrEmpty(mergedOptions.Instance)) - { - var mergedOptionsMonitor = _serviceProvider.GetService>(); - mergedOptionsMonitor?.Get(authenticationScheme); - } + // Question for Jenny: Shall we merge inside GetOptions? + _tokenAcquisitionHost.ProcessOptionsForAnonymousControllers(authenticationScheme, mergedOptions); if (string.IsNullOrEmpty(tenant)) { tenant = mergedOptions.TenantId; } - if (!string.IsNullOrEmpty(tenant) && _metaTenantIdentifiers.Contains(tenant)) + if (!string.IsNullOrEmpty(tenant) && _metaTenantIdentifiers.Contains(tenant!)) { throw new ArgumentException(IDWebErrorMessage.ClientCredentialTenantShouldBeTenanted, nameof(tenant)); } @@ -477,7 +401,7 @@ public async Task GetAccessTokenForAppAsync( /// for a downstream API using; /// 1) the token cache (for web apps and web APIs) if a token exists in the cache /// 2) or the on-behalf-of flow - /// in web APIs, for the user account that is ascertained from the claims provided in the + /// in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. /// instance of the current HttpContext. /// /// Scopes to request for the downstream API to call. @@ -515,155 +439,52 @@ await GetAuthenticationResultForUserAsync( return result.AccessToken; } - /// - /// Used in web APIs (no user interaction). - /// Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - /// the client, in turn, can trigger a user interaction so that the user consents to more scopes. - /// - /// Scopes to consent to. - /// The that triggered the challenge. - /// The to update. - /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - public Task ReplyForbiddenWithWwwAuthenticateHeaderAsync( - IEnumerable scopes, - MsalUiRequiredException msalServiceException, - HttpResponse? httpResponse = null) - { - ReplyForbiddenWithWwwAuthenticateHeader(scopes, msalServiceException, null, httpResponse); - return Task.CompletedTask; - } - - /// - /// Used in web APIs (no user interaction). - /// Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - /// the client, in turn, can trigger a user interaction so that the user consents to more scopes. - /// - /// Scopes to consent to. - /// The that triggered the challenge. - /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - /// The to update. - public void ReplyForbiddenWithWwwAuthenticateHeader( - IEnumerable scopes, - MsalUiRequiredException msalServiceException, - string? authenticationScheme = JwtBearerDefaults.AuthenticationScheme, - HttpResponse? httpResponse = null) - { - // A user interaction is required, but we are in a web API, and therefore, we need to report back to the client through a 'WWW-Authenticate' header https://tools.ietf.org/html/rfc6750#section-3.1 - string proposedAction = Constants.Consent; - if (msalServiceException.ErrorCode == MsalError.InvalidGrantError && AcceptedTokenVersionMismatch(msalServiceException)) - { - throw msalServiceException; - } - - authenticationScheme = GetEffectiveAuthenticationScheme(authenticationScheme); - MergedOptions mergedOptions = GetOptions(authenticationScheme); - - var application = GetOrBuildConfidentialClientApplication(mergedOptions); - - string consentUrl = $"{application.Authority}/oauth2/v2.0/authorize?client_id={mergedOptions.ClientId}" - + $"&response_type=code&redirect_uri={application.AppConfig.RedirectUri}" - + $"&response_mode=query&scope=offline_access%20{string.Join("%20", scopes)}"; - - IDictionary parameters = new Dictionary() - { - { Constants.ConsentUrl, consentUrl }, - { Constants.Claims, msalServiceException.Claims }, - { Constants.Scopes, string.Join(",", scopes) }, - { Constants.ProposedAction, proposedAction }, - }; - - string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\"")); - - httpResponse ??= CurrentHttpContext?.Response; - - if (httpResponse == null) - { - throw new InvalidOperationException(IDWebErrorMessage.HttpContextAndHttpResponseAreNull); - } - - var headers = httpResponse.Headers; - httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; - - headers[HeaderNames.WWWAuthenticate] = new StringValues($"{Constants.Bearer} {parameterString}"); - } - /// /// Removes the account associated with context.HttpContext.User from the MSAL.NET cache. /// - /// RedirectContext passed-in to a - /// OpenID Connect event. + /// User /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. /// A that represents a completed account removal operation. public async Task RemoveAccountAsync( - RedirectContext context, + ClaimsPrincipal user, string? authenticationScheme) { - ClaimsPrincipal user = context.HttpContext.User; string? userId = user.GetMsalAccountId(); if (!string.IsNullOrEmpty(userId)) { - authenticationScheme = GetEffectiveAuthenticationScheme(authenticationScheme); - MergedOptions mergedOptions = GetOptions(authenticationScheme); + authenticationScheme = _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); + MergedOptions mergedOptions = _tokenAcquisitionHost.GetOptions(authenticationScheme); IConfidentialClientApplication app = GetOrBuildConfidentialClientApplication(mergedOptions); if (mergedOptions.IsB2C) { - await _tokenCacheProvider.ClearAsync(userId).ConfigureAwait(false); + await _tokenCacheProvider.ClearAsync(userId!).ConfigureAwait(false); } else { - string? identifier = context.HttpContext.User.GetMsalAccountId(); + string? identifier = user.GetMsalAccountId(); IAccount account = await app.GetAccountAsync(identifier).ConfigureAwait(false); if (account != null) { await app.RemoveAsync(account).ConfigureAwait(false); - await _tokenCacheProvider.ClearAsync(userId).ConfigureAwait(false); + await _tokenCacheProvider.ClearAsync(userId!).ConfigureAwait(false); } } } } - /// - public string GetEffectiveAuthenticationScheme(string? authenticationScheme) - { - if (authenticationScheme != null) - { - return authenticationScheme; - } - else - { - return _serviceProvider.GetService()?.GetDefaultAuthenticateSchemeAsync()?.Result?.Name ?? - ((CurrentHttpContext?.GetTokenUsedToCallWebAPI() != null) - ? JwtBearerDefaults.AuthenticationScheme : OpenIdConnectDefaults.AuthenticationScheme); - } - } - private bool IsInvalidClientCertificateError(MsalServiceException exMsal) { return !_retryClientCertificate && string.Equals(exMsal.ErrorCode, Constants.InvalidClient, StringComparison.OrdinalIgnoreCase) && +#if !NET472 && !NET462 exMsal.Message.Contains(Constants.InvalidKeyError, StringComparison.OrdinalIgnoreCase); - } - - private string BuildCurrentUriFromRequest( - HttpContext httpContext, - HttpRequest request, - MergedOptions mergedOptions) - { - // need to lock to avoid threading issues with code outside of this library - // https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?#do-not-access-httpcontext-from-multiple-threads - lock (httpContext) - { - return UriHelper.BuildAbsolute( - request.Scheme, - request.Host, - request.PathBase, - mergedOptions.CallbackPath.Value ?? string.Empty); - } +#else + exMsal.Message.Contains(Constants.InvalidKeyError); +#endif } internal /* for testing */ IConfidentialClientApplication GetOrBuildConfidentialClientApplication( @@ -688,23 +509,7 @@ private string BuildCurrentUriFromRequest( /// private IConfidentialClientApplication BuildConfidentialClientApplication(MergedOptions mergedOptions) { - var httpContext = CurrentHttpContext; - var request = httpContext?.Request; - string? currentUri = null; - - if (!string.IsNullOrEmpty(mergedOptions.ConfidentialClientApplicationOptions.RedirectUri)) - { - currentUri = mergedOptions.ConfidentialClientApplicationOptions.RedirectUri; - } - - if (request != null && string.IsNullOrEmpty(currentUri)) - { - currentUri = BuildCurrentUriFromRequest( - httpContext!, - request, - mergedOptions); - } - + string? currentUri = _tokenAcquisitionHost.GetCurrentRedirectUri(mergedOptions); mergedOptions.PrepareAuthorityInstanceForMsal(); try @@ -748,7 +553,7 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged && (mergedOptions.ClientCertificates == null || !mergedOptions.ClientCertificates.Any()) && !string.IsNullOrWhiteSpace(mergedOptions.UserAssignedManagedIdentityClientId)) { - builder.WithClientAssertion(new MsiSignedAssertionProvider(mergedOptions.UserAssignedManagedIdentityClientId).GetSignedAssertion); + builder.WithClientAssertion(new MsiSignedAssertionProvider(mergedOptions.UserAssignedManagedIdentityClientId!).GetSignedAssertion); } if (mergedOptions.ClientCertificates != null) @@ -790,6 +595,8 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged } } + + private async Task GetAuthenticationResultForWebApiToCallDownstreamApiAsync( IConfidentialClientApplication application, string? tenantId, @@ -800,8 +607,8 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged try { // In web API, validatedToken will not be null - SecurityToken? validatedToken = CurrentHttpContext?.GetTokenUsedToCallWebAPI(); - + SecurityToken? validatedToken = _tokenAcquisitionHost.GetTokenUsedToCallWebAPI(); + // In the case the token is a JWE (encrypted token), we use the decrypted token. string? tokenUsedToCallTheWebApi = GetActualToken(validatedToken); @@ -819,7 +626,7 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged } else { - string? sessionKey = tokenAcquisitionOptions.LongRunningWebApiSessionKey; + string? sessionKey = tokenAcquisitionOptions!.LongRunningWebApiSessionKey; if (sessionKey == TokenAcquisitionOptions.LongRunningWebApiSessionKeyAuto) { sessionKey = null; @@ -835,7 +642,7 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged } else if (!string.IsNullOrEmpty(tokenAcquisitionOptions?.LongRunningWebApiSessionKey)) { - string sessionKey = tokenAcquisitionOptions.LongRunningWebApiSessionKey; + string sessionKey = tokenAcquisitionOptions!.LongRunningWebApiSessionKey!; builder = (application as ILongRunningWebApi)? .AcquireTokenInLongRunningProcess( scopes.Except(_scopesRequestedByMsal), @@ -862,7 +669,7 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged } } - ClaimsPrincipal? user = GetUserFromHttpContext(); + ClaimsPrincipal? user = _tokenAcquisitionHost.GetUserFromRequest(); if (user != null) { builder.WithCcsRoutingHint(user.GetObjectId(), user.GetTenantId()); @@ -1000,8 +807,11 @@ private Task GetAuthenticationResultForWebAppWithAccountFr { string b2cAuthority = application.Authority.Replace( new Uri(application.Authority).PathAndQuery, - $"/{ClaimConstants.Tfp}/{mergedOptions.Domain}/{userFlow ?? mergedOptions.DefaultUserFlow}", - StringComparison.OrdinalIgnoreCase); + $"/{ClaimConstants.Tfp}/{mergedOptions.Domain}/{userFlow ?? mergedOptions.DefaultUserFlow}" +#if !NET472 && !NET462 + ,StringComparison.OrdinalIgnoreCase +#endif + ); builder.WithB2CAuthority(b2cAuthority) .WithSendX5C(mergedOptions.SendX5C); @@ -1021,54 +831,11 @@ private static bool AcceptedTokenVersionMismatch(MsalUiRequiredException msalSer // way to distinguish the case. // This is subject to change in the future return msalServiceException.Message.Contains( - ErrorCodes.B2CPasswordResetErrorCode, - StringComparison.InvariantCulture); - } - - private ClaimsPrincipal? GetUserFromHttpContext() - { - var httpContext = CurrentHttpContext; - if (httpContext != null) - { - // Need to lock due to https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?#do-not-access-httpcontext-from-multiple-threads - lock (httpContext) - { - return httpContext.User; - } - } - - return null; - } - - private async Task GetAuthenticatedUserAsync(ClaimsPrincipal? user) - { - if (user == null) - { - user = GetUserFromHttpContext(); - } - - if (user == null) - { - try - { - AuthenticationStateProvider? authenticationStateProvider = - _serviceProvider.GetService(typeof(AuthenticationStateProvider)) - as AuthenticationStateProvider; - - if (authenticationStateProvider != null) - { - // AuthenticationState provider is only available in Blazor - AuthenticationState state = await authenticationStateProvider.GetAuthenticationStateAsync().ConfigureAwait(false); - user = state.User; - } - } - catch - { - // do nothing. - } - } - - return user; + ErrorCodes.B2CPasswordResetErrorCode +#if !NET472 && !NET462 + ,StringComparison.InvariantCulture +#endif + ); } private void Log( @@ -1120,5 +887,10 @@ private void Log( return null; } } + + public string GetEffectiveAuthenticationScheme(string? authenticationScheme) + { + return _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); + } } } diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs b/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs new file mode 100644 index 000000000..ea40df97e --- /dev/null +++ b/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs @@ -0,0 +1,208 @@ +using System; +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Identity.Client; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.Identity.Web +{ + /// + /// Implementation of ITokenAcquisitionHost in the case of ASP.NET Core + /// + internal class TokenAcquisitionAspnetCoreHost : ITokenAcquisitionHost + { + private readonly IHttpContextAccessor _httpContextAccessor; + private HttpContext? CurrentHttpContext => _httpContextAccessor.HttpContext; + private readonly IOptionsMonitor _mergedOptionsMonitor; + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + + + /// + /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to + /// configure the confidential client application and a token cache provider. + /// This constructor is called by ASP.NET Core dependency injection. + /// + /// Access to the HttpContext of the request. + /// Configuration options. + /// Logger. + /// Service provider. + public TokenAcquisitionAspnetCoreHost( + IHttpContextAccessor httpContextAccessor, + IOptionsMonitor mergedOptionsMonitor, + ILogger logger, + IServiceProvider serviceProvider) + { + _httpContextAccessor = httpContextAccessor; + _mergedOptionsMonitor = mergedOptionsMonitor; + _logger = logger; + _serviceProvider = serviceProvider; + } + + public MergedOptions GetOptions(string authenticationScheme) + { + var mergedOptions = _mergedOptionsMonitor.Get(authenticationScheme); + if (!mergedOptions.MergedWithCca) + { + var ccaOptionsMonitor = _serviceProvider.GetService>(); + ccaOptionsMonitor?.Get(authenticationScheme); + var bearerMonitor = _serviceProvider.GetService>(); // supports event handlers + bearerMonitor?.Get(authenticationScheme); + } + + DefaultCertificateLoader.UserAssignedManagedIdentityClientId = mergedOptions.UserAssignedManagedIdentityClientId; + return mergedOptions; + } + + public void ProcessOptionsForAnonymousControllers(string? authenticationScheme, MergedOptions mergedOptions) + { + // Case of an anonymous controller, no [Authorize] attribute will trigger the merge options + if (string.IsNullOrEmpty(mergedOptions.Instance)) + { + var mergedOptionsMonitor = _serviceProvider.GetService>(); + mergedOptionsMonitor?.Get(authenticationScheme); + } + // Case of an anonymous controller called from a web app + if (string.IsNullOrEmpty(mergedOptions.Instance)) + { + var mergedOptionsMonitor = _serviceProvider.GetService>(); + mergedOptionsMonitor?.Get(authenticationScheme); + } + } + + public void SetSession(string key, string value) + { + CurrentHttpContext?.Session.SetString(key, value); + } + + /// + public string GetEffectiveAuthenticationScheme(string? authenticationScheme) + { + if (authenticationScheme != null) + { + return authenticationScheme; + } + else + { + return _serviceProvider.GetService()?.GetDefaultAuthenticateSchemeAsync()?.Result?.Name ?? + ((CurrentHttpContext?.GetTokenUsedToCallWebAPI() != null) + ? JwtBearerDefaults.AuthenticationScheme : OpenIdConnectDefaults.AuthenticationScheme); + } + } + + public string? GetCurrentRedirectUri(MergedOptions mergedOptions) + { + var httpContext = CurrentHttpContext; + var request = httpContext?.Request; + string? currentUri = null; + + if (!string.IsNullOrEmpty(mergedOptions.ConfidentialClientApplicationOptions.RedirectUri)) + { + currentUri = mergedOptions.ConfidentialClientApplicationOptions.RedirectUri; + } + + if (request != null && string.IsNullOrEmpty(currentUri)) + { + currentUri = BuildCurrentUriFromRequest( + httpContext!, + request, + mergedOptions); + } + + return currentUri; + } + + private string BuildCurrentUriFromRequest( + HttpContext httpContext, + HttpRequest request, + MergedOptions mergedOptions) + { + // need to lock to avoid threading issues with code outside of this library + // https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?#do-not-access-httpcontext-from-multiple-threads + lock (httpContext) + { + return UriHelper.BuildAbsolute( + request.Scheme, + request.Host, + request.PathBase, + mergedOptions.CallbackPath.Value ?? string.Empty); + } + } + + public void SetHttpResponse(HttpStatusCode statusCode, string wwwAuthenticate) + { + var httpResponse = CurrentHttpContext?.Response; + if (httpResponse == null) + { + throw new InvalidOperationException(IDWebErrorMessage.HttpContextAndHttpResponseAreNull); + } + + var headers = httpResponse.Headers; + httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; + + headers[HeaderNames.WWWAuthenticate] = wwwAuthenticate; + } + + public SecurityToken? GetTokenUsedToCallWebAPI() + { + return CurrentHttpContext?.GetTokenUsedToCallWebAPI(); + } + + public ClaimsPrincipal? GetUserFromRequest() + { + var httpContext = CurrentHttpContext; + if (httpContext != null) + { + // Need to lock due to https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?#do-not-access-httpcontext-from-multiple-threads + lock (httpContext) + { + return httpContext.User; + } + } + + return null; + } + + public async Task GetAuthenticatedUserAsync(ClaimsPrincipal? user) + { + if (user == null) + { + user = GetUserFromRequest(); + } + + if (user == null) + { + try + { + AuthenticationStateProvider? authenticationStateProvider = + _serviceProvider.GetService(typeof(AuthenticationStateProvider)) + as AuthenticationStateProvider; + + if (authenticationStateProvider != null) + { + // AuthenticationState provider is only available in Blazor + AuthenticationState state = await authenticationStateProvider.GetAuthenticationStateAsync().ConfigureAwait(false); + user = state.User; + } + } + catch + { + // do nothing. + } + } + + return user; + } + } +} diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs index c251d8e4d..82bff3837 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs @@ -13,6 +13,19 @@ namespace Microsoft.Identity.Web /// public class TokenAcquisitionOptions { + /// + /// Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps + /// in the cases where a given account is a guest in other tenants, and you want to acquire tokens + /// for a specific tenant. + /// + /// Can be the tenant ID or domain name. + public string? Tenant { get; set; } + + /// + /// Uses a particular user flow (in the case of AzureAD B2C) + /// + public string? UserFlow { get; set; } + /// /// Sets the correlation id to be used in the authentication request /// to the /token endpoint. diff --git a/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilder.cs b/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilder.cs index e798a1973..ff97e8d59 100644 --- a/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilder.cs +++ b/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilder.cs @@ -172,7 +172,7 @@ internal static void WebAppCallsWebApiImplementation( { // Remove the account from MSAL.NET token cache var tokenAcquisition = context!.HttpContext.RequestServices.GetRequiredService(); - await tokenAcquisition.RemoveAccountAsync(context, openIdConnectScheme).ConfigureAwait(false); + await tokenAcquisition.RemoveAccountAsync(context!.HttpContext.User, openIdConnectScheme).ConfigureAwait(false); await signOutHandler(context).ConfigureAwait(false); }; }); diff --git a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs index 91edb30bd..2528242a6 100644 --- a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs @@ -769,7 +769,7 @@ private async Task AddMicrosoftIdentityWebAppCallsWebApi_TestRedirectToIdentityP // Assert original RedirectToIdentityProviderForSignOut event and TokenAcquisition method were called await redirectFuncMock.ReceivedWithAnyArgs().Invoke(Arg.Any()).ConfigureAwait(false); - await tokenAcquisitionMock.ReceivedWithAnyArgs().RemoveAccountAsync(Arg.Any()).ConfigureAwait(false); + await tokenAcquisitionMock.ReceivedWithAnyArgs().RemoveAccountAsync(Arg.Any().HttpContext.User).ConfigureAwait(false); } private (HttpContext, AuthenticationScheme, AuthenticationProperties) CreateContextParameters(IServiceProvider provider) From 51110b18715584888ce2bf1b0ff9207c2dad1c70 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Wed, 16 Mar 2022 17:32:26 -0700 Subject: [PATCH 02/48] Updating the tests. --- .../Microsoft.Identity.Web.xml | 2922 +++-------------- .../AcquireTokenForAppIntegrationTests.cs | 10 +- .../ServiceCollectionExtensionsTests.cs | 7 + .../TokenAcquisitionAuthorityTests.cs | 9 +- .../WebAppExtensionsTests.cs | 3 +- 5 files changed, 394 insertions(+), 2557 deletions(-) diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 7b7f5713d..77daa0234 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -4,326 +4,6 @@ Microsoft.Identity.Web - - - Extension methods for . - - - - - Creates the from the values found - in an . - - The instance. - A built from . - - - - Extension methods related to App Services authentication (Easy Auth). - - - - - Add authentication with App Services. - - Authentication builder. - The builder, to chain commands. - - - - Default values related to AppServiceAuthentication handler. - - - - - The default value used for AppServiceAuthenticationOptions.AuthenticationScheme. - - - - - App service authentication handler. - - - - - Constructor for the AppServiceAuthenticationHandler. - Note the parameters are required by the base class. - - App service authentication options. - Logger factory. - URL encoder. - System clock. - - - - - - - Information about the App Services configuration on the host. - - - - - Is App Services authentication enabled?. - - - - - Logout URL for App Services Auth web sites. - - - - - ClientID of the App Services Auth web site. - - - - - Client secret of the App Services Auth web site. - - - - - Issuer of the App Services Auth web site. - - - - - Get headers from environment to help debugging App Services authentication. - - - - - Get the ID token from the headers sent by App services authentication. - - Headers. - The ID Token. - - - - Get the IDP from the headers sent by App services authentication. - - Headers. - The IDP. - - - - Get the user claims from the headers and environment variables. - - Headers. - User claims. - - - - Options for Azure App Services authentication. - - - - - Implementation of ITokenAcquisition for App Services authentication (EasyAuth). - - - - - Please call GetOrCreateApplication instead of accessing this field directly. - - - - - Constructor of the AppServicesAuthenticationTokenAcquisition. - - The App token cache provider. - Access to the HttpContext of the request. - HTTP client factory. - - - - - - - - - - - - - - - - - - - - - - - - - Filter used on a controller action to trigger incremental consent. - - - The following controller action will trigger. - - [AuthorizeForScopes(Scopes = new[] {"Mail.Send"})] - public async Task<IActionResult> SendEmail() - { - } - - - - - - Scopes to request. - - - - - Key section on the configuration file that holds the scope value. - - - - - Azure AD B2C user flow. - - - - - Allows specifying an AuthenticationScheme if OpenIdConnect is not the default challenge scheme. - - - - - Handles the . - - Context provided by ASP.NET Core. - - - - Finds an MsalUiRequiredException in one of the inner exceptions. - - Exception from which we look for an MsalUiRequiredException. - The MsalUiRequiredException if there is one, null, otherwise. - - - - Extensions for . - - - - - Enables an Azure Function to act as/expose a protected web API, enabling bearer token authentication. Calling this method from your Azure function validates the token and exposes the identity of the user or app on behalf of which your function is called, in the HttpContext.User member, where your function can make use of it. - - The current HTTP Context, such as req.HttpContext. - A task indicating success or failure. In case of failure . - - - - Azure SDK token credential based on the ITokenAcquisition service. - - - - - Constructor from an ITokenAcquisition service. - - Token acquisition. - - - - - - - - - - Factory class to create objects. - - - - - Instantiate a from an account object ID and tenant ID. This can - be useful when the web app subscribes to another service on behalf of the user - and then is called back by a notification where the user is identified by their tenant - ID and object ID (like in Microsoft Graph Web Hooks). - - Tenant ID of the account. - Object ID of the account in this tenant ID. - A containing these two claims. - - - - private async Task GetChangedMessagesAsync(IEnumerable<Notification> notifications) - { - HttpContext.User = ClaimsPrincipalExtension.FromTenantIdAndObjectId(subscription.TenantId, - subscription.UserId); - foreach (var notification in notifications) - { - SubscriptionStore subscription = - subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); - string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); - ...} - } - - - - - - Client assertion. - - - - - Constructor of a ClientAssertion, which can be used instead - of a client secret or client certificates to authenticate the - confidential client application. See https://aka.ms/ms-id-web/client-assertions. - - Signed assertion. - Optional expiry. - - - - Signed assertion. - - - - - Expiry of the client assertion. - - - - - Description of a client assertion in the application configuration. - See https://aka.ms/ms-id-web/client-assertions. - - - - - Constructor of a ClientAssertionDescription. - - delegate providing the client assertion - when it is necessary. - - - - delegate to get the client assertion. - - - - - Client assertion. - - - - - Get the signed assertion (and refreshes it if needed). - - Cancellation token. - The signed assertion. - - - - Expiry of the client assertion. - - Constants for claim types. @@ -489,776 +169,342 @@ Constants related to the log messages. - - - Extension class containing cookie policies (work around for same site). - - - + - Handles SameSite cookie issue according to the https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1. - The default list of user agents that disallow "SameSite=None", - was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + Encoding table. - to update. - to chain. - + - Handles SameSite cookie issue according to the docs: https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1 - The default list of user agents that disallow "SameSite=None", was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + The following functions perform base64url encoding which differs from regular base64 encoding as follows + * padding is skipped so the pad character '=' doesn't have to be percent encoded + * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') + The changes make the encoding alphabet file and URL safe. - to update. - If you don't want to use the default user agent list implementation, - the method sent in this parameter will be run against the user agent and if returned true, SameSite value will be set to Unspecified. - The default user agent list used can be found at: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. - to chain. + string to encode. + Base64Url encoding of the UTF8 bytes. - + - Checks if the specified user agent supports "SameSite=None" cookies. + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. - Browser user agent. - - Incompatible user agents include: - - Versions of Chrome from Chrome 51 to Chrome 66 (inclusive on both ends). - Versions of UC Browser on Android prior to version 12.13.2. - Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. - - Reference: https://www.chromium.org/updates/same-site/incompatible-clients. - - True, if the user agent does not allow "SameSite=None" cookie; otherwise, false. + An array of 8-bit unsigned integers. + The number of elements of inArray to convert. + An offset in inArray. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. - + - Implementation for the downstream web API. + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. + An array of 8-bit unsigned integers. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. - + - Constructor. - - Token acquisition service. - Named options provider. - HTTP client. - Configuration options. - - - - - - - - - + Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. + base64Url encoded string. + UTF8 bytes. - + - Merge the options from configuration and override from caller. + Decodes the string from Base64UrlEncoded to UTF8. - Named configuration. - Delegate to override the configuration. + string to decode. + UTF8 string. - + - Extension methods to support downstream web API services. + EventIds for Logging. - + - Adds a named downstream web API service related to a specific configuration section. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - Builder. - Name of the configuration for the service. - This is the name used when calling the service from controller/pages. - Configuration. - The builder for chaining. - + - Adds a named downstream web API service initialized with delegates. + Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps + in the cases where a given account is a guest in other tenants, and you want to acquire tokens + for a specific tenant. - Builder. - Name of the configuration for the service. - This is the name which will be used when calling the service from controller/pages. - Action to configure the options. - The builder for chaining. + Can be the tenant ID or domain name. - + - Extensions for the downstream web API. + Uses a particular user flow (in the case of AzureAD B2C) - + - Get a strongly typed response from the web API. + Sets the correlation id to be used in the authentication request + to the /token endpoint. - Output type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Path to the API endpoint relative to the base URL specified in the configuration. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A strongly typed response from the web API. - - - - Calls the web API with an HttpPost, providing strongly typed input and getting - strongly typed output. - - Output type. - Input type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Path to the API endpoint relative to the base URL specified in the configuration. - Input data sent to the API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A strongly typed response from the web API. - - - - Calls the web API endpoint with an HttpPut, providing strongly typed input data. - - Input type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Path to the API endpoint relative to the base URL specified in the configuration. - Input data sent to the API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The value returned by the downstream web API. - - - - Calls the web API endpoint with an HttpPut, provinding strongly typed input data - and getting back strongly typed data. - - Output type. - Input type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Path to the API endpoint relative to the base URL specified in the configuration. - Input data sent to the API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A strongly typed response from the web API. - - - - Call a web API endpoint with an HttpGet, - and return strongly typed data. - - Output type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The value returned by the downstream web API. - - - - Call a web API with a strongly typed input, with an HttpGet. - - Input type. - The downstream web API. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Input data. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The value returned by the downstream web API. - + - Options passed-in to call downstream web APIs. To call Microsoft Graph, see rather - MicrosoftGraphOptions in the Microsoft.Identity.Web.MicrosoftGraph assembly. + Sets Extra Query Parameters for the query string in the HTTP authentication request. - + - Base URL for the called downstream web API. For instance "https://graph.microsoft.com/beta/".. + A string with one or multiple claims to request. + Normally used with Conditional Access. - + - Path relative to the (for instance "me"). + Specifies if the token request will ignore the access token in the token cache + and will attempt to acquire a new access token. + If true, the request will ignore the token cache. The default is false. + Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, + for it induces performance degradation, as the token cache is not utilized. - + - HTTP method used to call this downstream web API (by default Get). + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. - + - Provides an opportunity to customize the HttpRequestMessage. For example, - to customize the headers. This is called after the message was formed, including - the Authorization header, and just before the message is sent. + Cancellation token to be used when calling the token acquisition methods. - + - Clone the options (to be able to override them). + Key used for long running web APIs that need to call downstream web + APIs on behalf of the user. Can be null, if you are not developing a long + running web API, if you want + Microsoft.Identity.Web to allocate a session key for you, or your own string + if you want to associate the session with some information you have externally + (for instance a Microsoft Graph hook identifier). - A clone of the options. - + - Return the downstream web API URL. + Value that can be used for so that + MSAL.NET allocates the long running web api session key for the developer. - URL of the downstream web API. - + Clone the options (to be able to override them). A clone of the options. - - - Interface used to call a downstream web API, for instance from controllers. - - - - - Calls the downstream web API for the user, based on a description of the - downstream web API in the configuration. - - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful on platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - HTTP context in the case where is - , , . - An that the application will process. - - - - Calls the downstream web API for the user, based on a description of the - downstream web API in the configuration. - - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful on platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - HTTP context in the case where is - , , . - An that the application will process. - - - - Calls a downstream web API consuming JSON with some data and returns data. - - Input type. - Output type. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Input parameter to the downstream web API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - The value returned by the downstream web API. - - A list method that returns an IEnumerable<MyItem>>. - - public Task<IEnumerable<MyItem>> GetAsync() - { - return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( - ServiceName, - null, - options => - { - options.RelativePath = $"api/todolist"; - }); - } - - - Example of editing. - - public Task<MyItem> EditAsync(MyItem myItem) - { - return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( - ServiceName, - nyItem, - options => - { - options.HttpMethod = HttpMethod.Patch; - options.RelativePath = $"api/todolist/{myItem.Id}"; - }); - } - - - - - - Calls a downstream web API consuming JSON with some data and returns data. - - Input type. - Output type. - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Input parameter to the downstream web API. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Overrides the options proposed in the configuration described - by . - [Optional] Claims representing a user. This is useful in platforms like Blazor - or Azure Signal R, where the HttpContext is not available. In other platforms, the library - will find the user from the HttpContext. - The value returned by the downstream web API. - - A list method that returns an IEnumerable<MyItem>>. - - public Task<IEnumerable<MyItem>> GetAsync() - { - return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( - ServiceName, - null, - options => - { - options.RelativePath = $"api/todolist"; - }); - } - - - Example of editing. - - public Task<MyItem> EditAsync(MyItem myItem) - { - return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( - ServiceName, - nyItem, - options => - { - options.HttpMethod = HttpMethod.Patch; - options.RelativePath = $"api/todolist/{myItem.Id}"; - }); - } - - - - - - Calls the downstream web API for the app, with the required scopes. - - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Overrides the options proposed in the configuration described - by . - HTTP content in the case where is - , , . - An that the application will process. - - - - Calls the downstream web API for the app, with the required scopes. - - Name of the service describing the downstream web API. There can - be several configuration named sections mapped to a , - each for one downstream web API. You can pass-in null, but in that case - needs to be set. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Overrides the options proposed in the configuration described - by . - HTTP content in the case where is - , , . - An that the application will process. - - - - A DelegatingHandler implementation that add an authorization header with a token for the application. - - - - - Initializes a new instance of the class. - - Token acquisition service. - Named options provider. - Name of the service describing the downstream web API. - - - - - - - Base class for Microsoft Identity authentication message handlers. - - - - - Gets the token acquisition service. - - - - - Initializes a new instance of the class. - - Token acquisition service. - Named options provider. - Name of the service describing the downstream web API. - - - - Gets the options for the specified request. - - The request. - The configured options. - - - - Base options passed-in to authenticate with Microsoft Identity. - - - - - Space separated scopes required to call the downstream web API. - For instance "user.read mail.read". - - - - - [Optional] tenant ID. This is used for specific scenarios where - the application needs to call a downstream web API on behalf of a user in several tenants. - It would mostly be used from code, not from the configuration. - - - - - [Optional]. User flow (in the case of a B2C downstream web API). If not - specified, the B2C downstream web API will be called with the default user flow from - . - - - - - Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), - rather than a Bearer token. - PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, - which MSAL can manage. See https://aka.ms/msal-net-pop. - Set to true to enable PoP tokens automatically. - - - - - Options passed-in to create the token acquisition object which calls into MSAL .NET. - - - + - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + OpenIdConnect options - + - Returns the scopes. + Gets or sets the Authority to use when making OpenIdConnect calls. - Scopes. - + - Extension for IHttpClientBuilder for startup initialization of Microsoft Identity authentication handlers. + Client secret used to authenticate a confidential client app to AAD + (alternatively use client certificates) - + - Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. + Gets or sets the 'client_id' (application ID) as appears in the + application registration. - Builder. - Name of the configuration for the service. - Configuration. - The builder for chaining. - + - Adds a named Microsoft Identity user authentication message handler initialized with delegates. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - Builder. - Name of the configuration for the service. - Action to configure the options. - The builder for chaining. - + - Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. + Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". - Builder. - Name of the configuration for the service. - Configuration. - The builder for chaining. - + - Adds a named Microsoft Identity application authentication message handler initialized with delegates. + Gets or sets the tenant ID. - Builder. - Name of the configuration for the service. - Action to configure the options. - The builder for chaining. - + - Options passed-in to Microsoft Identity message handlers. + Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. - + - Clone the options (to be able to override them). + Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. - A clone of the options. - + - Clone the options (to be able to override them). + Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. - A clone of the options. - + - A DelegatingHandler implementation that add an authorization header with a token on behalf of the current user. + Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. - + - Initializes a new instance of the class. + Gets the default user flow (which is signUpsignIn). - Token acquisition service. - Named options provider. - Configuration options. - Name of the service describing the downstream web API. - - - - + - Extension methods. + Enables legacy ADAL cache serialization and deserialization. + Performance improvements when working with MSAL only apps. + Set to true if you have a shared cache with ADAL apps. + The default is false. - - Determines whether the specified string collection contains any. - The search for. - The string collection. - - true if the specified string collection contains any; otherwise, false. - - + - Keep the validated token associated with the HTTP request. + Is considered B2C if the attribute SignUpSignInPolicyId is defined. - HTTP context. - Token to preserve after the token is validated so that - it can be used in the actions. - + - Get the parsed information about the token used to call the web API. + Is considered to have client credentials if the attribute ClientCertificates + or ClientSecret is defined. - HTTP context associated with the current request. - used to call the web API. - + - Provides access to get or set the current error status. - The default implementation will use TempData and be enabled when run under Development. + Description of the certificates used to prove the identity of the web app or web API. + An example in the appsetting.json: + + "ClientCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + - + - Gets the error message for the current request. + Description of the certificates used to decrypt an encrypted token in a web API. - Current . - The current error message if available. + An example in the appsetting.json: + + "TokenDecryptionCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + - + - Sets the error message for the current request. + Specifies if the x5c claim (public key of the certificate) should be sent to the STS. + Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: + this method will send the public certificate to Azure AD along with the token request, + so that Azure AD can use it to validate the subject name based on a trusted issuer policy. + This saves the application admin from the need to explicitly manage the certificate rollover + (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. - Current . - Error message to set. + The default is false. - + - Gets whether error messages should be displayed. + Requests an auth code for the frontend (SPA using MSAL.js for instance). + See https://aka.ms/msal-net/spa-auth-code for details. + The default is false. - + - Helper methods to handle incremental consent and conditional access in - a web app. + Daemon applications can validate a token based on roles, or using the ACL-based authorization + pattern to control tokens without a roles claim. If using ACL-based authorization, + Microsoft Identity Web will not throw if roles or scopes are not in the Claims. + For details see https://aka.ms/ms-identity-web/daemon-ACL. + The default is false. - + - Can the exception be solved by re-signing-in the user?. + Used, when deployed to Azure, to specify explicitly a user assigned managed identity. + See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. - Exception from which the decision will be made. - Returns true if the issue can be solved by signing-in - the user, and false, otherwise. - + - Build authentication properties needed for incremental consent. + Sets the ResetPassword route path. + Defaults to /MicrosoftIdentity/Account/ResetPassword, + which is the value used by Microsoft.Identity.Web.UI. - Scopes to request. - instance. - User. - Userflow being invoked for AAD B2C. - AuthenticationProperties. - + - An implementation of IConfigurationRetriever geared towards Azure AD issuers metadata. + Sets the Error route path. + Defaults to the value /MicrosoftIdentity/Account/Error, + which is the value used by Microsoft.Identity.Web.UI. - - Retrieves a populated configuration given an address and an . - Address of the discovery document. - The to use to read the discovery document. - A cancellation token that can be used by other objects or threads to receive notice of cancellation. . - - A that, when completed, returns from the configuration. - - address - Azure AD Issuer metadata address URL is required - or retriever - No metadata document retriever is provided. - - + - Model class to hold information parsed from the Azure AD issuer endpoint. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. - + - Issuer associated with the OIDC endpoint. + Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). - + - Interface for the token acquisition service (encapsulating MSAL.NET). + In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user + signed-in and consented) + the token redeemed from the "/> is added to the cache, so that it can then be used to acquire another token on-behalf-of the + same user in order to call to downstream APIs. + Scopes to request. Can be empty + Authorization code + Authentication scheme to use (config section) + Client Info obtained with the code + PKCE code verifier + User flow in the case of B2C + The ID Token. - + - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. - Scopes to request for the downstream API to call. + Signed in user Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An to call on behalf of the user, the downstream API characterized by its scopes. - - - - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An authentication result for the app itself, based on its scopes. + A that represents a completed remove from cache operation. Interface for the token acquisition service (encapsulating MSAL.NET). - - - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. - - Scopes to request for the downstream API to call. - Enables to override the tenant/account for the same identity. This is useful in the - cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant. - Azure AD B2C UserFlow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token to call on behalf of the user, the downstream API characterized by its scopes. - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -1276,21 +522,6 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token to call on behalf of the user, the downstream API characterized by its scopes. - - - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the current user - - Scopes to request for the downstream API to call. - Enables to override the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Azure AD B2C UserFlow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An to call on behalf of the user, the downstream API characterized by its scopes. - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -1308,21 +539,6 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An to call on behalf of the user, the downstream API characterized by its scopes. - - - Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token for the app itself, based on its scopes. - Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -1340,21 +556,6 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token for the app itself, based on its scopes. - - - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An authentication result for the app itself, based on its scopes. - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -1372,1672 +573,291 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An authentication result for the app itself, based on its scopes. - - - Used in web APIs (which therefore cannot have an interaction with the user). - Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that - the client can trigger an interaction with the user so the user can consent to more scopes. - - Scopes to consent to. - triggering the challenge. - The to update. - - + - Used in web APIs (which therefore cannot have an interaction with the user). - Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that - the client can trigger an interaction with the user so the user can consent to more scopes. - - Scopes to consent to. - triggering the challenge. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The to update. - - - - Get the effective authentication scheme based on the context. + Get the effective authentication scheme based on the context. Proposed authentication scheme. Effective authenticationScheme which is the authentication scheme if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - + - Used in web APIs (which therefore cannot have an interaction with the user). - Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that - the client can trigger an interaction with the user so the user can consent to more scopes. + Token acquisition service. - Scopes to consent to. - triggering the challenge. - The to update. - A representing the asynchronous operation. - - - Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). + LoggingMessage class for TokenAcquisition. - - - In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user - signed-in and consented) - An On-behalf-of token contained in the is added to the cache, so that it can then be used to acquire another token on-behalf-of the - same user in order to call to downstream APIs. - - The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. - Scopes to request. - Authentication scheme to use. - A that represents a completed add to cache operation. - - From the configuration of the Authentication of the ASP.NET Core web API: - OpenIdConnectOptions options; - - Subscribe to the authorization code received event: - - options.Events = new OpenIdConnectEvents(); - options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; - } - - - And then in the OnAuthorizationCodeRecieved method, call : - - private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) - { - var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); - await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); - } - - - - + - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. - Signed in user - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - A that represents a completed remove from cache operation. - + - EventIds for Logging. + Scopes which are already requested by MSAL.NET. They should not be re-requested;. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. + Meta-tenant identifiers which are not allowed in client credentials. - + - Base class for web app and web API Microsoft Identity authentication - builders. + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + The App token cache provider. + Host of the token acquisition. + HTTP client factory. + Logger. + Service provider. - + - Constructor. + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from claims provided in the current claims principal. + instance of the current HttpContext. - The services being configured. - Optional configuration section. + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Options passed-in to create the token acquisition options object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - + - The services being configured. + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. - + - Configuration section from which to bind options. + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - It can be null if the configuration happens with delegates - rather than configuration. + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. - + - Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. + instance of the current HttpContext. + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - + - Add the incremental consent and conditional access handler for Blazor - server side pages. + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. - Service side blazor builder. - The builder. + User + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A that represents a completed account removal operation. - + - Add the incremental consent and conditional access handler for - web app pages, Razor pages, controllers, views, etc... + Creates an MSAL confidential client application. - Service collection. - The service collection. - + - Handler for Blazor specific APIs to handle incremental consent - and conditional access. + Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + . + Claims principal for the user on behalf of whom to get a token. + Scopes for the downstream API to call. + (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user described in the claimsPrincipal. + Merged options. + Azure AD B2C user flow to target. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Initializes a new instance of the class. + Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. - Service provider to get the HttpContextAccessor for the current HttpContext, when available. + . + User IAccount for which to acquire a token. + See . + Scopes for the downstream API to call. + TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user. + Merged options. + Azure AD B2C user flow. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Boolean to determine if server is Blazor. + Logger for handling MSAL exceptions in TokenAcquisition. + ILogger. + Specific log message from TokenAcquisition. + Exception from MSAL.NET. - + - Current user. + Logger for handling information specific to MSAL in token acquisition. + ILogger. + durationTotalInMs. + durationInHttpInMs. + durationInCacheInMs. + cache or IDP. + correlationId. + cacheRefreshReason. + Exception from MSAL.NET. - + - Base URI to use in forming the redirect. + Prototype of certificate-less authentication using a signed assertion + acquired with MSI (federated identity). + The signed assertion. - + - For Blazor/Razor pages to process the exception from - a user challenge. + Client assertion. - Exception. - + - Forces the user to consent to specific scopes and perform - Conditional Access to get specific claims. Use on a Razor/Blazor - page or controller to proactively ensure the scopes and/or claims - before acquiring a token. The other mechanism - ensures claims and scopes requested by Azure AD after a failed token acquisition attempt. - See https://aka.ms/ms-id-web/ca_incremental-consent for details. + Constructor of a ClientAssertion, which can be used instead + of a client secret or client certificates to authenticate the + confidential client application. See https://aka.ms/ms-id-web/client-assertions. - Scopes to request. - Claims to ensure. - Userflow being invoked for AAD B2C. + Signed assertion. + Optional expiry. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Signed assertion. - + - Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". + Expiry of the client assertion. - + - Gets or sets the tenant ID. + Description of a client assertion in the application configuration. + See https://aka.ms/ms-id-web/client-assertions. - + - Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. + Constructor of a ClientAssertionDescription. + delegate providing the client assertion + when it is necessary. - + - Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. + delegate to get the client assertion. - + - Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. + Client assertion. - + - Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. + Get the signed assertion (and refreshes it if needed). + Cancellation token. + The signed assertion. - + - Gets the default user flow (which is signUpsignIn). + Expiry of the client assertion. - + - Enables legacy ADAL cache serialization and deserialization. - Performance improvements when working with MSAL only apps. - Set to true if you have a shared cache with ADAL apps. + Microsoft Identity Web specific exception class for + use in Blazor or Razor pages to process the user challenge. + Handles the . - The default is false. - + - Is considered B2C if the attribute SignUpSignInPolicyId is defined. + Exception thrown by MSAL when a user challenge is encountered. - + - Is considered to have client credentials if the attribute ClientCertificates - or ClientSecret is defined. + Scopes to request. - + - Description of the certificates used to prove the identity of the web app or web API. + Specified userflow. - An example in the appsetting.json: - - "ClientCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - - + - Description of the certificates used to decrypt an encrypted token in a web API. - - An example in the appsetting.json: - - "TokenDecryptionCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - - - - - Specifies if the x5c claim (public key of the certificate) should be sent to the STS. - Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: - this method will send the public certificate to Azure AD along with the token request, - so that Azure AD can use it to validate the subject name based on a trusted issuer policy. - This saves the application admin from the need to explicitly manage the certificate rollover - (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. - - The default is false. - - - - Requests an auth code for the frontend (SPA using MSAL.js for instance). - See https://aka.ms/msal-net/spa-auth-code for details. - - The default is false. - - - - Daemon applications can validate a token based on roles, or using the ACL-based authorization - pattern to control tokens without a roles claim. If using ACL-based authorization, - Microsoft Identity Web will not throw if roles or scopes are not in the Claims. - For details see https://aka.ms/ms-identity-web/daemon-ACL. - - The default is false. - - - - Used, when deployed to Azure, to specify explicitly a user assigned managed identity. - See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. - - - - - Sets the ResetPassword route path. - Defaults to /MicrosoftIdentity/Account/ResetPassword, - which is the value used by Microsoft.Identity.Web.UI. - - - - - Sets the Error route path. - Defaults to the value /MicrosoftIdentity/Account/Error, - which is the value used by Microsoft.Identity.Web.UI. - - - - - Microsoft Identity Web specific exception class for - use in Blazor or Razor pages to process the user challenge. - Handles the . - - - - - Exception thrown by MSAL when a user challenge is encountered. - - - - - Scopes to request. - - - - - Specified userflow. - - - - - Handles the user challenge for Blazor or Razor pages. + Handles the user challenge for Blazor or Razor pages. Exception thrown by MSAL when a user challenge is encountered. Scopes to request. Userflow used in B2C. - - - Prototype of certificate-less authentication using a signed assertion - acquired with MSI (federated identity). - - The signed assertion. - - - - This is the metadata that describes required auth scopes for a given endpoint - in a web API. It's the underlying data structure the requirement will look for - in order to validate scopes in the scope claims. - - - - - Scopes accepted by this web API. - - - - - Fully qualified name of the configuration key containing the required scopes (separated - by spaces). - - - - - Extensions for building the RequiredScope policy during application startup. - - - - services.AddAuthorization(o => - { o.AddPolicy("Custom", - policyBuilder =>policyBuilder.RequireScope("access_as_user")); - }); - - - - - - Adds a to the current instance which requires - that the current user has the specified claim and that the claim value must be one of the allowed values. - - Used for building policies during application startup. - Values the claim must process one or more of for evaluation to succeed. - A reference to this instance after the operation has completed. - - - - Adds a to the current instance which requires - that the current user has the specified claim and that the claim value must be one of the allowed values. - - Used for building policies during application startup. - Values the claim must process one or more of for evaluation to succeed. - A reference to this instance after the operation has completed. - - - - This attribute is used on a controller, pages, or controller actions - to declare (and validate) the scopes required by a web API. These scopes can be declared - in two ways: hardcoding them, or declaring them in the configuration. Depending on your - choice, use either one or the other of the constructors. - For details, see https://aka.ms/ms-id-web/required-scope-attribute. - - - - - Scopes accepted by this web API. - - - - - Fully qualified name of the configuration key containing the required scopes (separated - by spaces). - - - If the appsettings.json file contains a section named "AzureAd", in which - a property named "Scopes" contains the required scopes, the attribute on the - controller/page/action to protect should be set to the following: - - [RequiredScope(RequiredScopesConfigurationKey="AzureAd:Scopes")] - - - - - - Verifies that the web API is called with the right scopes. - If the token obtained for this API is on behalf of the authenticated user does not have - any of these in its scope claim, the - method updates the HTTP response providing a status code 403 (Forbidden) - and writes to the response body a message telling which scopes are expected in the token. - - Scopes accepted by this web API. - When the scopes don't match, the response is a 403 (Forbidden), - because the user is authenticated (hence not 401), but not authorized. - - Add the following attribute on the controller/page/action to protect: - - - [RequiredScope("access_as_user")] - - - and - if you want to express the required scopes from the configuration. - - - - Default constructor. - - - - [RequiredScope(RequiredScopesConfigurationKey="AzureAD:Scope")] - class Controller : BaseController - { - } - - - - - - Unused: Compatibility of interface with the Authorization Filter. - - - - - Interface implemented by diagnostics for the JWT Bearer middleware. - - - - - Called to subscribe to . - - JWT Bearer events. - The events (for chaining). - - - - Diagnostics used in the OpenID Connect middleware - (used in web apps). - - - - - Method to subscribe to . - - OpenID Connect events. - - - - Diagnostics for the JwtBearer middleware (used in web APIs). - - - - - Constructor for a . This constructor - is used by dependency injection. - - Logger. - - - - Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. - - - - - Invoked when a protocol message is first received. - - - - - Invoked after the security token has passed validation and a ClaimsIdentity has been generated. - - - - - Invoked before a challenge is sent back to the caller. - - - - - Subscribes to all the JwtBearer events, to help debugging, while - preserving the previous handlers (which are called). - - Events to subscribe to. - for chaining. - - - - Factory class for creating the IssuerValidator per authority. - - - - - Initializes a new instance of the class. - - Options passed-in to create the AadIssuerValidator object. - HttpClientFactory. - - - - Gets an for an authority. - - The authority to create the validator for, e.g. https://login.microsoftonline.com/. - A for the aadAuthority. - if is null or empty. - - - - Diagnostics used in the OpenID Connect middleware - (used in web apps). - - - - - Constructor of the , used - by dependency injection. - - Logger used to log the diagnostics. - - - - Invoked before redirecting to the identity provider to authenticate. This can - be used to set ProtocolMessage.State that will be persisted through the authentication - process. The ProtocolMessage can also be used to add or customize parameters - sent to the identity provider. - - - - - Invoked when a protocol message is first received. - - - - - Invoked after security token validation if an authorization code is present - in the protocol message. - - - - - Invoked after "authorization code" is redeemed for tokens at the token endpoint. - - - - - Invoked when an IdToken has been validated and produced an AuthenticationTicket. - - - - - Invoked when user information is retrieved from the UserInfoEndpoint. - - - - - Invoked if exceptions are thrown during request processing. The exceptions will - be re-thrown after this event unless suppressed. - - - - - Invoked when a request is received on the RemoteSignOutPath. - - - - - Invoked before redirecting to the identity provider to sign out. - - - - - Invoked before redirecting to the Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutRedirectUri - at the end of a remote sign-out flow. - - - - - Subscribes to all the OpenIdConnect events, to help debugging, while - preserving the previous handlers (which are called). - - Events to subscribe to. - - - - Generic class that registers the token audience from the provided Azure AD authority. - - - - - Default validation of the audience: - - when registering an Azure AD web API in the app registration portal (and adding a scope) - the default App ID URI generated by the portal is api://{clientID} - - However, the audience (aud) of the token acquired to access this web API is different depending - on the "accepted access token version" for the web API: - - if accepted token version is 1.0, the audience provided in the token - by the Microsoft identity platform (formerly Azure AD v2.0) endpoint is: api://{ClientID} - - if the accepted token version is 2.0, the audience provided by Azure AD v2.0 in the token - is {CliendID} - When getting an access token for an Azure AD B2C web API the audience in the token is - api://{ClientID}. - - When web API developers don't provide the "Audience" in the configuration, Microsoft.Identity.Web - considers that this is the default App ID URI as explained above. When developer provides the - "Audience" member, it's available in the TokenValidationParameter.ValidAudience. - - Audiences in the security token. - Security token from which to validate the audiences. - Token validation parameters. - True if the token is valid; false, otherwise. - - - - Extension class providing the extension methods for that - can be used in web APIs to validate the roles in controller actions. - - - - - When applied to an , verifies that the application - has the expected roles. - - HttpContext (from the controller). - Roles accepted by this web API. - When the roles don't match, the response is a 403 (Forbidden), - because the app does not have the expected roles. - - - - Extension class providing the extension - methods for that - can be used in web APIs to validate scopes in controller actions. - We recommend using instead the RequiredScope Attribute on the controller, the page or the action. - See https://aka.ms/ms-id-web/required-scope-attribute. - - - - - When applied to an , verifies that the user authenticated in the - web API has any of the accepted scopes. - If there is no authenticated user, the response is a 401 (Unauthenticated). - If the authenticated user does not have any of these , the - method updates the HTTP response providing a status code 403 (Forbidden) - and writes to the response body a message telling which scopes are expected in the token. - We recommend using instead the RequiredScope Attribute on the controller, the page or the action. - See https://aka.ms/ms-id-web/required-scope-attribute. - - HttpContext (from the controller). - Scopes accepted by this web API. - - - - Extensions for building the required scope attribute during application startup. - - - - - This method adds support for the required scope attribute. It adds a default policy that - adds a scope requirement. This requirement looks for IAuthRequiredScopeMetadata on the current endpoint. - - The services being configured. - Services. - - - - This method adds metadata to route endpoint to describe required scopes. It's the imperative version of - the [RequiredScope] attribute. - - Class implementing . - To customize the endpoints. - Scope. - Builder. - - - - RequireScopeOptions. - - - - - Sets the default policy. - - - - - - - - Scope authorization handler that needs to be called for a specific requirement type. - In this case, . - - - - - Constructor for the scope authorization handler, which takes a configuration. - - Configuration. - - - - Makes a decision if authorization is allowed based on a specific requirement. - - AuthorizationHandlerContext. - Scope authorization requirement. - Task. - - - - Implements an - which requires at least one instance of the specified claim type, and, if allowed values are specified, - the claim value must be any of the allowed values. - - - - - Creates a new instance of . - - The optional list of scope values. - - - - Gets the optional list of scope values. - - - - - Gets the optional list of scope values from configuration. - - - - - - - - Options passed-in to create the AadIssuerValidator object. - - - - - Sets the name of the HttpClient to get from the IHttpClientFactory for use with the configuration manager. - Needed when customizing the client such as configuring a proxy. - - - - - Extensions for IServiceCollection for startup initialization of web APIs. - - - - - Add the token acquisition service. - - Service collection. - Specifies if an instance of should be a singleton. - The service collection. - - This method is typically called from the ConfigureServices(IServiceCollection services) in Startup.cs. - Note that the implementation of the token cache can be chosen separately. - - - // Token acquisition service and its cache implementation as a session cache - services.AddTokenAcquisition() - .AddDistributedMemoryCache() - .AddSession() - .AddSessionBasedTokenCache(); - - - - - - An implementation of that uses to track error messages. - - - - - Token acquisition service. - - - LoggingMessage class for TokenAcquisition. - - - - - Constructor of the TokenAcquisition service. This requires the Azure AD Options to - configure the confidential client application and a token cache provider. - This constructor is called by ASP.NET Core dependency injection. - - The App token cache provider. - Host of the token acquisition service. - HTTP client factory. - Logger. - Service provider. - - - - Used in web APIs (no user interaction). - Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - the client, in turn, can trigger a user interaction so that the user consents to more scopes. - - Scopes to consent to. - The that triggered the challenge. - The to update. - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - - - - Used in web APIs (no user interaction). - Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that - the client, in turn, can trigger a user interaction so that the user consents to more scopes. - - Scopes to consent to. - The that triggered the challenge. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - The to update. - - - - This handler is executed after the authorization code is received (once the user signs-in and consents) during the - authorization code flow in a web app. - It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. - The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the - on-behalf-of flow for the signed-in user's account, - in order to call to downstream APIs. - - The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. - scopes to request access to. - Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). - - From the configuration of the Authentication of the ASP.NET Core web API: - OpenIdConnectOptions options; - - Subscribe to the authorization code received event: - - options.Events = new OpenIdConnectEvents(); - options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; - } - - - And then in the OnAuthorizationCodeRecieved method, call : - - private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) - { - var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); - await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); - } - - - - - - Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. - - - - - Scopes which are already requested by MSAL.NET. They should not be re-requested;. - - - - - Meta-tenant identifiers which are not allowed in client credentials. - - - - - Constructor of the TokenAcquisition service. This requires the Azure AD Options to - configure the confidential client application and a token cache provider. - This constructor is called by ASP.NET Core dependency injection. - - The App token cache provider. - Host of the token acquisition. - HTTP client factory. - Logger. - Service provider. - - - - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from claims provided in the current claims principal. - instance of the current HttpContext. - - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Options passed-in to create the token acquisition options object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - - - - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An authentication result for the app itself, based on its scopes. - - - - Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token for the app itself, based on its scopes. - - - - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. - instance of the current HttpContext. - - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - - - - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. - - User - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A that represents a completed account removal operation. - - - - Creates an MSAL confidential client application. - - - - - Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. - - . - Claims principal for the user on behalf of whom to get a token. - Scopes for the downstream API to call. - (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user described in the claimsPrincipal. - Merged options. - Azure AD B2C user flow to target. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - - - - Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. - - . - User IAccount for which to acquire a token. - See . - Scopes for the downstream API to call. - TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user. - Merged options. - Azure AD B2C user flow. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - - - - Logger for handling MSAL exceptions in TokenAcquisition. - - ILogger. - Specific log message from TokenAcquisition. - Exception from MSAL.NET. - - - - Logger for handling information specific to MSAL in token acquisition. - - ILogger. - durationTotalInMs. - durationInHttpInMs. - durationInCacheInMs. - cache or IDP. - correlationId. - cacheRefreshReason. - Exception from MSAL.NET. - - - - Implementation of ITokenAcquisitionHost in the case of ASP.NET Core - - - - - Constructor of the TokenAcquisition service. This requires the Azure AD Options to - configure the confidential client application and a token cache provider. - This constructor is called by ASP.NET Core dependency injection. - - Access to the HttpContext of the request. - Configuration options. - Logger. - Service provider. - - - - - - - Options passed-in to create the token acquisition object which calls into MSAL .NET. - - - - - Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps - in the cases where a given account is a guest in other tenants, and you want to acquire tokens - for a specific tenant. - - Can be the tenant ID or domain name. - - - - Uses a particular user flow (in the case of AzureAD B2C) - - - - - Sets the correlation id to be used in the authentication request - to the /token endpoint. - - - - - Sets Extra Query Parameters for the query string in the HTTP authentication request. - - - - - A string with one or multiple claims to request. - Normally used with Conditional Access. - - - - - Specifies if the token request will ignore the access token in the token cache - and will attempt to acquire a new access token. - If true, the request will ignore the token cache. The default is false. - Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, - for it induces performance degradation, as the token cache is not utilized. - - - - - Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), - rather than a Bearer token. - PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, - which MSAL can manage. See https://aka.ms/msal-net-pop. - - - - - Cancellation token to be used when calling the token acquisition methods. - - - - - Key used for long running web APIs that need to call downstream web - APIs on behalf of the user. Can be null, if you are not developing a long - running web API, if you want - Microsoft.Identity.Web to allocate a session key for you, or your own string - if you want to associate the session with some information you have externally - (for instance a Microsoft Graph hook identifier). - - - - - Value that can be used for so that - MSAL.NET allocates the long running web api session key for the developer. - - - - - Clone the options (to be able to override them). - - A clone of the options. - - - - An implementation of token cache for confidential clients backed by an HTTP session. - - - For this session cache to work effectively, the ASP.NET Core session has to be configured properly. - The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state - - In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: - - services.AddSession(option => - { - option.Cookie.IsEssential = true; - }); - - In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: - - app.UseSession(); // Before UseMvc() - - - https://aka.ms/msal-net-token-cache-serialization - - LoggingMessage class for MsalSessionTokenCacheProvider. - - - - - MSAL Token cache provider constructor. - - Session for the current user. - Logger. - - - - Read a blob representing the token cache from its key. - - Key representing the token cache - (account or app). - Read blob. - - - - Read a blob representing the token cache from its key. - - Key representing the token cache - (account or app). - Hints for the cache serialization implementation optimization. - Read blob. - - - - Writes the token cache identified by its key to the serialization mechanism. - - Key for the cache (account ID or app ID). - Blob to write to the cache. - A that completes when a write operation has completed. - - - - Removes a cache described by its key. - - Key of the token cache (user account or app ID). - A that completes when key removal has completed. - - - - LoggingMessage class for MsalSessionTokenCacheProvider. - - - - - Session cache logging. - - ILogger. - /// Cache operation (Read, Write, etc...). - Session Id. - MSAL.NET cache key. - Exception. - - - - Session cache deserialized. - - ILogger. - MSAL.NET cache key. - Session Id. - Exception. - - - - Extension class to add a session token cache serializer to MSAL. - - - - - Adds an HTTP session-based application token cache to the service collection. - - - For this session cache to work effectively the ASP.NET Core session has to be configured properly. - The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. - - In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: - - services.AddSession(option => - { - option.Cookie.IsEssential = true; - }); - - In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: - - app.UseSession(); // Before UseMvc() - - Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). - - The services collection to add to. - The service collection. - - - - Adds an HTTP session-based per-user token cache to the service collection. - - - For this session cache to work effectively the ASP.NET Core session has to be configured properly. - The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. - - In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: - - services.AddSession(option => - { - option.Cookie.IsEssential = true; - }); - - In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: - - app.UseSession(); // Before UseMvc() - - Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). - - The services collection to add to. - The service collection. - - - - Encoding table. - - - - - The following functions perform base64url encoding which differs from regular base64 encoding as follows - * padding is skipped so the pad character '=' doesn't have to be percent encoded - * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') - The changes make the encoding alphabet file and URL safe. - - string to encode. - Base64Url encoding of the UTF8 bytes. - - - - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. - - An array of 8-bit unsigned integers. - The number of elements of inArray to convert. - An offset in inArray. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. - - - - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. - - An array of 8-bit unsigned integers. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. - - - - Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. - base64Url encoded string. - UTF8 bytes. - - - - Decodes the string from Base64UrlEncoded to UTF8. - - string to decode. - UTF8 string. - - - - Authentication builder for a web API. - - - - - Constructor. - - The services being configured. - Default scheme used for OpenIdConnect. - ACtion called to configure the JwtBearer options. - Action called to configure - the Microsoft identity options. - Configuration section from which to - get parameters. - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - - The action to configure . - The authentication builder to chain. - - - - Extensions for for startup initialization of web APIs. - - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - - The to which to add this configuration. - The configuration instance. - The configuration section with the necessary settings to initialize authentication options. - The JWT bearer scheme name to be used. By default it uses "Bearer". - - Set to true if you want to debug, or just understand the JWT bearer events. - - The authentication builder to chain. - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - - The to which to add this configuration. - The configuration second from which to fill-in the options. - The JWT bearer scheme name to be used. By default it uses "Bearer". - - Set to true if you want to debug, or just understand the JWT bearer events. - - The authentication builder to chain. - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - - The to which to add this configuration. - The action to configure . - The action to configure the . - The JWT bearer scheme name to be used. By default it uses "Bearer". - - Set to true if you want to debug, or just understand the JWT bearer events. - The authentication builder to chain. - - - - Builder for web API authentication with configuration. - - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). - This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - - The authentication builder to chain. - - - - Extension for IServiceCollection for startup initialization of web APIs. - - - - - Protects the web API with Microsoft identity platform (formerly Azure AD v2.0) - This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - - Service collection to which to add authentication. - The Configuration object. - The configuration section with the necessary settings to initialize authentication options. - The JwtBearer scheme name to be used. By default it uses "Bearer". - - Set to true if you want to debug, or just understand the JwtBearer events. - The authentication builder to chain extension methods. - - - - Web API authentication builder. - - - - - Allows a higher level abstraction of security token (i.e. System.IdentityModel.Tokens.Jwt and more modern, Microsoft.IdentityModel.JsonWebTokens) - to be used with Microsoft Identity Web. - Developers should continue to use `EnableTokenAcquisitionToCallDownstreamApi`. - This API is not considered part of the public API and may change. - - The action to configure . - Authentication scheme. - The services being configured. - IConfigurationSection. - The authentication builder to chain. - - - - Authentication builder returned by the EnableTokenAcquisitionToCallDownstreamApi methods - enabling you to decide token cache implementations. - - - - - Add in memory token caches. - - to configure. - to configure. - the service collection. - - - - Add distributed token caches. - - the service collection. - - - - Add session token caches. - - the service collection. - - - - Authentication builder specific for Microsoft identity platform. - - - - - Constructor. - - The services being configured. - Default scheme used for OpenIdConnect. - Action called to configure - the Microsoft identity options. - Optional configuration section. - - - - The web app calls a web API. - - Initial scopes. - The builder itself for chaining. - - - - The web app calls a web API. This override enables you to specify the - ConfidentialClientApplicationOptions (from MSAL.NET) programmatically. - - Action to configure the - MSAL.NET confidential client application options. - Initial scopes. - The builder itself for chaining. - - - - Extensions for the for startup initialization. - - - - - Add authentication to a web app with Microsoft identity platform. - This method expects the configuration file will have a section, named "AzureAd" as default, - with the necessary settings to initialize authentication options. - - The to which to add this configuration. - The configuration instance. - The configuration section with the necessary settings to initialize authentication options. - The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". - The cookie-based scheme name to be used. By default it uses "Cookies". - Set to true if you want to debug, or just understand the OpenID Connect events. - A display name for the authentication handler. - The builder for chaining. - - - - Add authentication with Microsoft identity platform. - This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. - - The to which to add this configuration. - The configuration section from which to get the options. - The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". - The cookie-based scheme name to be used. By default it uses "Cookies". - Set to true if you want to debug, or just understand the OpenID Connect events. - A display name for the authentication handler. - The authentication builder for chaining. - - - - Add authentication with Microsoft identity platform. - - The to which to add this configuration. - The action to configure . - The action to configure . - The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". - The cookie-based scheme name to be used. By default it uses "Cookies". - Set to true if you want to debug, or just understand the OpenID Connect events. - A display name for the authentication handler. - The authentication builder for chaining. - - - - Add authentication with Microsoft identity platform. - - The to which to add this configuration. - The action to configure . - The action to configure . - The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". - The cookie-based scheme name to be used. By default it uses "Cookies". - Set to true if you want to debug, or just understand the OpenID Connect events. - A display name for the authentication handler. - Configuration section. - The authentication builder for chaining. - - - - Add authentication with Microsoft identity platform. - - The to which to add this configuration. - The action to configure . - The action to configure . - The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". - The cookie-based scheme name to be used. By default it uses "Cookies". - Set to true if you want to debug, or just understand the OpenID Connect events. - A display name for the authentication handler. - The authentication builder for chaining. - - - - Builder for a Microsoft identity web app authentication where configuration is - available for EnableTokenAcquisitionToCallDownstreamApi. - - - - - Constructor. - - The services being configured. - Default scheme used for OpenIdConnect. - Action called to configure - the Microsoft identity options. - Optional configuration section. - - - - Add support for the web app to acquire tokens to call an API. - - Optional initial scopes to request. - The authentication builder for chaining. - - - - Extension for IServiceCollection for startup initialization. - - - - - Add authentication with Microsoft identity platform. - This method expects the configuration file will have a section, (by default named "AzureAd"), with the necessary settings to - initialize the authentication options. - - Service collection to which to add authentication. - The IConfiguration object. - The name of the configuration section with the necessary - settings to initialize authentication options. - Optional name for the open id connect authentication scheme - (by default OpenIdConnectDefaults.AuthenticationScheme). This can be specified when you want to support - several OpenIdConnect identity providers. - Optional name for the cookie authentication scheme - (by default OpenIdConnectDefaults.AuthenticationScheme). - Set to true if you want to debug, or just understand the OpenIdConnect events. - A display name for the authentication handler. - The authentication builder to chain extension methods. - diff --git a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs index b49fb2257..a014a2b23 100644 --- a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs +++ b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs @@ -253,14 +253,18 @@ private void InitializeTokenAcquisitionObjects() _provider.GetService(), _provider.GetService>()); + var tokenAcquisitionAspnetCoreHost = new TokenAcquisitionAspnetCoreHost( + MockHttpContextAccessor.CreateMockHttpContextAccessor(), + _provider.GetService>(), + _provider.GetService>(), + _provider); _tokenAcquisition = new TokenAcquisition( _msalTestTokenCacheProvider, - MockHttpContextAccessor.CreateMockHttpContextAccessor(), - _provider.GetService>(), _provider.GetService(), _provider.GetService>(), + tokenAcquisitionAspnetCoreHost, _provider); - _tokenAcquisition.GetOptions(OpenIdConnectDefaults.AuthenticationScheme); + tokenAcquisitionAspnetCoreHost.GetOptions(OpenIdConnectDefaults.AuthenticationScheme); } private void BuildTheRequiredServices() diff --git a/tests/Microsoft.Identity.Web.Test/ServiceCollectionExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/ServiceCollectionExtensionsTests.cs index 5356a149c..ecf44f040 100644 --- a/tests/Microsoft.Identity.Web.Test/ServiceCollectionExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/ServiceCollectionExtensionsTests.cs @@ -37,6 +37,13 @@ public void AddTokenAcquisition_AddsWithCorrectLifetime() Assert.Null(actual.ImplementationFactory); }, actual => + { + Assert.Equal(ServiceLifetime.Scoped, actual.Lifetime); + Assert.Equal(typeof(ITokenAcquisitionHost), actual.ServiceType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => { Assert.Equal(ServiceLifetime.Scoped, actual.Lifetime); Assert.Equal(typeof(ITokenAcquisitionInternal), actual.ServiceType); diff --git a/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs b/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs index 96d6877b6..ead88a2cd 100644 --- a/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs +++ b/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs @@ -22,20 +22,25 @@ namespace Microsoft.Identity.Web.Test public class TokenAcquisitionAuthorityTests { private TokenAcquisition _tokenAcquisition; + private TokenAcquisitionAspnetCoreHost _tokenAcquisitionAspnetCoreHost; private ServiceProvider _provider; private IOptionsMonitor _applicationOptionsMonitor; private IOptionsMonitor _microsoftIdentityOptionsMonitor; private void InitializeTokenAcquisitionObjects() { + _tokenAcquisitionAspnetCoreHost = new TokenAcquisitionAspnetCoreHost( + MockHttpContextAccessor.CreateMockHttpContextAccessor(), + _provider.GetService>(), + _provider.GetService>(), + _provider); _tokenAcquisition = new TokenAcquisition( new MsalTestTokenCacheProvider( _provider.GetService(), _provider.GetService>()), - MockHttpContextAccessor.CreateMockHttpContextAccessor(), - _provider.GetService>(), _provider.GetService(), _provider.GetService>(), + _tokenAcquisitionAspnetCoreHost, _provider); } diff --git a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs index 2528242a6..2eb9aa5f8 100644 --- a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; @@ -769,7 +770,7 @@ private async Task AddMicrosoftIdentityWebAppCallsWebApi_TestRedirectToIdentityP // Assert original RedirectToIdentityProviderForSignOut event and TokenAcquisition method were called await redirectFuncMock.ReceivedWithAnyArgs().Invoke(Arg.Any()).ConfigureAwait(false); - await tokenAcquisitionMock.ReceivedWithAnyArgs().RemoveAccountAsync(Arg.Any().HttpContext.User).ConfigureAwait(false); + await tokenAcquisitionMock.ReceivedWithAnyArgs().RemoveAccountAsync(Arg.Any()).ConfigureAwait(false); } private (HttpContext, AuthenticationScheme, AuthenticationProperties) CreateContextParameters(IServiceProvider provider) From 8a0968b922679ead4a7071880e55ed48b55c718f Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Wed, 16 Mar 2022 21:56:21 -0700 Subject: [PATCH 03/48] - Adding a plain dotnet token acquisition host. - AddTokenAcquisition now callable from net462 - AddGraph calllable from net462 - Adding a daemon sample using Id.Web. --- Microsoft.Identity.Web.sln | 14 +- .../GraphServiceCollectionExtensions.cs | 54 + ...crosoft.Identity.Web.MicrosoftGraph.csproj | 3 +- .../MicrosoftGraphExtensions.cs | 32 +- .../PlainDotNetTokenAcquisitionHost.cs | 75 + src/Microsoft.Identity.Web/ITokenAcquirer.cs | 13 +- .../Microsoft.Identity.Web.csproj | 1 + .../Microsoft.Identity.Web.xml | 2934 ++++++++++++++--- .../MicrosoftIdentityOptions.cs | 2 +- .../ServiceCollectionExtensions.cs | 14 +- .../TokenAcquisitionOptions.cs | 2 + tests/daemon-app/Daemon-app/Daemon-app.csproj | 27 + tests/daemon-app/Daemon-app/Program.cs | 75 + tests/daemon-app/Daemon-app/appsettings.json | 12 + 14 files changed, 2842 insertions(+), 416 deletions(-) create mode 100644 src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs create mode 100644 src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs create mode 100644 tests/daemon-app/Daemon-app/Daemon-app.csproj create mode 100644 tests/daemon-app/Daemon-app/Program.cs create mode 100644 tests/daemon-app/Daemon-app/appsettings.json diff --git a/Microsoft.Identity.Web.sln b/Microsoft.Identity.Web.sln index 7c8456894..3905f2541 100644 --- a/Microsoft.Identity.Web.sln +++ b/Microsoft.Identity.Web.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29009.5 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web", "src\Microsoft.Identity.Web\Microsoft.Identity.Web.csproj", "{FD55C071-48D1-4FE8-8B1D-773E067FEC91}" EndProject @@ -134,6 +134,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Toke EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Certificate", "src\Microsoft.Identity.Web.Certificate\Microsoft.Identity.Web.Certificate.csproj", "{1E0B96CD-FDBF-482C-996A-775F691D984E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Daemon", "Daemon", "{5D970909-98CF-415C-A598-D60B4B4AD8A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Daemon-app", "tests\daemon-app\Daemon-app\Daemon-app.csproj", "{8C557C25-25C9-45B6-B2D3-77093893EBC5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -300,6 +304,10 @@ Global {1E0B96CD-FDBF-482C-996A-775F691D984E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E0B96CD-FDBF-482C-996A-775F691D984E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E0B96CD-FDBF-482C-996A-775F691D984E}.Release|Any CPU.Build.0 = Release|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -354,6 +362,8 @@ Global {D3A42D78-8A23-44EB-9A1F-63CF58B56AAC} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} {15B7F1B3-A438-4A42-BFFA-E3382543C037} = {D3A42D78-8A23-44EB-9A1F-63CF58B56AAC} {5B9AD363-6A27-4AF2-939E-B20E7C941ADF} = {20CDB9B2-D237-413A-8E76-A973597748B0} + {5D970909-98CF-415C-A598-D60B4B4AD8A9} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} + {8C557C25-25C9-45B6-B2D3-77093893EBC5} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4FA8C4C-3251-41CC-939B-7892F5798549} diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs new file mode 100644 index 000000000..1103bbc86 --- /dev/null +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Graph; + +namespace Microsoft.Identity.Web +{ + public static class GraphServiceCollectionExtensions + { + public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services) + { + services.AddTokenAcquisition(); + services.AddHttpClient(); + return services.AddMicrosoftGraph(options => { }); + } + + public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, Action configureMicrosoftGraphOptions) + { + // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests + services.AddOptions().Configure(configureMicrosoftGraphOptions); + + services.AddScoped(serviceProvider => + { + var tokenAquisitionService = serviceProvider.GetRequiredService(); + var options = serviceProvider.GetRequiredService>(); + + var microsoftGraphOptions = options.Value; + if (microsoftGraphOptions.Scopes == null) + { + throw new ArgumentException(IDWebErrorMessage.CalledApiScopesAreNull); + } + + string graphBaseUrl = microsoftGraphOptions.BaseUrl; + string[] initialScopes = microsoftGraphOptions.Scopes.Split(' '); + + GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? + new GraphServiceClient(new TokenAcquisitionAuthenticationProvider( + tokenAquisitionService, + new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })) : + new GraphServiceClient(graphBaseUrl, + new TokenAcquisitionAuthenticationProvider( + tokenAquisitionService, + new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })); + return client; + }); + return services; + } + } + +} diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj index a91fed1ec..726ae026c 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj @@ -62,11 +62,12 @@ - netcoreapp3.1; net5.0 + netcoreapp3.1; net5.0; net462; net472 true ../../build/MSAL.snk true enable + 10.0 diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs b/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs index 56c12608c..299cb8eac 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs @@ -11,6 +11,7 @@ namespace Microsoft.Identity.Web { +#if !NET472 && !NET462 /// /// Extensions methods on a MicrosoftIdentityAppCallingWebApiAuthenticationBuilder builder /// to add support to call Microsoft Graph. @@ -66,36 +67,12 @@ public static MicrosoftIdentityAppCallsWebApiAuthenticationBuilder AddMicrosoftG throw new ArgumentNullException(nameof(builder)); } - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - builder.Services.AddOptions().Configure(configureMicrosoftGraphOptions); - - builder.Services.AddScoped(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetRequiredService(); - var options = serviceProvider.GetRequiredService>(); - - var microsoftGraphOptions = options.Value; - if (microsoftGraphOptions.Scopes == null) - { - throw new ArgumentException(IDWebErrorMessage.CalledApiScopesAreNull); - } - - string graphBaseUrl = microsoftGraphOptions.BaseUrl; - string[] initialScopes = microsoftGraphOptions.Scopes.Split(' '); - - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionAuthenticationProvider( - tokenAquisitionService, - new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })) : - new GraphServiceClient(graphBaseUrl, - new TokenAcquisitionAuthenticationProvider( - tokenAquisitionService, - new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })); - return client; - }); + builder.Services.AddMicrosoftGraph(configureMicrosoftGraphOptions); return builder; } + + /// /// Add support to call Microsoft Graph. /// @@ -150,4 +127,5 @@ public static MicrosoftIdentityAppCallsWebApiAuthenticationBuilder AddMicrosoftG return builder; } } +#endif } diff --git a/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs b/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs new file mode 100644 index 000000000..ddfa88ee1 --- /dev/null +++ b/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs @@ -0,0 +1,75 @@ +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Microsoft.Identity.Client; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.Identity.Web.Hosts +{ + internal class PlainDotNetTokenAcquisitionHost : ITokenAcquisitionHost + { + + IOptionsMonitor _optionsMonitor; + IOptionsMonitor _mergedOptionsMonitor; + IOptionsMonitor _ccaOptionsMonitor; + + public PlainDotNetTokenAcquisitionHost( + IOptionsMonitor optionsMonitor, + IOptionsMonitor mergedOptionsMonitor, + IOptionsMonitor ccaOptionsMonitor) + { + _optionsMonitor = optionsMonitor; + _mergedOptionsMonitor = mergedOptionsMonitor; + _ccaOptionsMonitor = ccaOptionsMonitor; + } + + Task ITokenAcquisitionHost.GetAuthenticatedUserAsync(ClaimsPrincipal? user) + { + return null; + } + + string? ITokenAcquisitionHost.GetCurrentRedirectUri(MergedOptions mergedOptions) + { + return null; + } + + string ITokenAcquisitionHost.GetEffectiveAuthenticationScheme(string? authenticationScheme) + { + return authenticationScheme!; + } + + MergedOptions ITokenAcquisitionHost.GetOptions(string authenticationScheme) + { + var mergedOptions = _mergedOptionsMonitor.Get(authenticationScheme); + MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions( + /*authenticationScheme == null ? _optionsMonitor.CurrentValue :*/ _optionsMonitor.Get(authenticationScheme), + mergedOptions); + + DefaultCertificateLoader.UserAssignedManagedIdentityClientId = mergedOptions.UserAssignedManagedIdentityClientId; + return mergedOptions; + } + + SecurityToken? ITokenAcquisitionHost.GetTokenUsedToCallWebAPI() + { + return null; + } + + ClaimsPrincipal? ITokenAcquisitionHost.GetUserFromRequest() + { + return null; + } + + void ITokenAcquisitionHost.ProcessOptionsForAnonymousControllers(string authenticationScheme, MergedOptions mergedOptions) + { + } + + void ITokenAcquisitionHost.SetHttpResponse(HttpStatusCode statusCode, string wwwAuthenticate) + { + } + + void ITokenAcquisitionHost.SetSession(string key, string value) + { + } + } +} diff --git a/src/Microsoft.Identity.Web/ITokenAcquirer.cs b/src/Microsoft.Identity.Web/ITokenAcquirer.cs index c6d03a615..9520e3af4 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquirer.cs +++ b/src/Microsoft.Identity.Web/ITokenAcquirer.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; @@ -21,18 +20,18 @@ public interface ITokenAcquirer /// member of the controller's parameter. /// /// Scopes to request for the downstream API to call. + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. /// Optional claims principal representing the user. If not provided, will use the signed-in /// user (in a web app), or the user for which the token was received (in a web API) /// cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// An to call on behalf of the user, the downstream API characterized by its scopes. Task GetAuthenticationResultForUserAsync( IEnumerable scopes, + TokenAcquisitionOptions? tokenAcquisitionOptions = null, string? authenticationScheme = null, - ClaimsPrincipal? user = null, - TokenAcquisitionOptions? tokenAcquisitionOptions = null); + ClaimsPrincipal? user = null); /// /// Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -43,13 +42,13 @@ Task GetAuthenticationResultForUserAsync( /// Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration /// in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use /// several calls to get tokens for other resources). + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// An authentication result for the app itself, based on its scopes. Task GetAuthenticationResultForAppAsync( string scope, - string? authenticationScheme = null, - TokenAcquisitionOptions? tokenAcquisitionOptions = null); + TokenAcquisitionOptions? tokenAcquisitionOptions = null, + string? authenticationScheme = null); } } diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj index 7afb1d134..b1333ae34 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj @@ -131,6 +131,7 @@ + diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 77daa0234..cfe4d9a67 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -4,6 +4,326 @@ Microsoft.Identity.Web + + + Extension methods for . + + + + + Creates the from the values found + in an . + + The instance. + A built from . + + + + Extension methods related to App Services authentication (Easy Auth). + + + + + Add authentication with App Services. + + Authentication builder. + The builder, to chain commands. + + + + Default values related to AppServiceAuthentication handler. + + + + + The default value used for AppServiceAuthenticationOptions.AuthenticationScheme. + + + + + App service authentication handler. + + + + + Constructor for the AppServiceAuthenticationHandler. + Note the parameters are required by the base class. + + App service authentication options. + Logger factory. + URL encoder. + System clock. + + + + + + + Information about the App Services configuration on the host. + + + + + Is App Services authentication enabled?. + + + + + Logout URL for App Services Auth web sites. + + + + + ClientID of the App Services Auth web site. + + + + + Client secret of the App Services Auth web site. + + + + + Issuer of the App Services Auth web site. + + + + + Get headers from environment to help debugging App Services authentication. + + + + + Get the ID token from the headers sent by App services authentication. + + Headers. + The ID Token. + + + + Get the IDP from the headers sent by App services authentication. + + Headers. + The IDP. + + + + Get the user claims from the headers and environment variables. + + Headers. + User claims. + + + + Options for Azure App Services authentication. + + + + + Implementation of ITokenAcquisition for App Services authentication (EasyAuth). + + + + + Please call GetOrCreateApplication instead of accessing this field directly. + + + + + Constructor of the AppServicesAuthenticationTokenAcquisition. + + The App token cache provider. + Access to the HttpContext of the request. + HTTP client factory. + + + + + + + + + + + + + + + + + + + + + + + + + Filter used on a controller action to trigger incremental consent. + + + The following controller action will trigger. + + [AuthorizeForScopes(Scopes = new[] {"Mail.Send"})] + public async Task<IActionResult> SendEmail() + { + } + + + + + + Scopes to request. + + + + + Key section on the configuration file that holds the scope value. + + + + + Azure AD B2C user flow. + + + + + Allows specifying an AuthenticationScheme if OpenIdConnect is not the default challenge scheme. + + + + + Handles the . + + Context provided by ASP.NET Core. + + + + Finds an MsalUiRequiredException in one of the inner exceptions. + + Exception from which we look for an MsalUiRequiredException. + The MsalUiRequiredException if there is one, null, otherwise. + + + + Extensions for . + + + + + Enables an Azure Function to act as/expose a protected web API, enabling bearer token authentication. Calling this method from your Azure function validates the token and exposes the identity of the user or app on behalf of which your function is called, in the HttpContext.User member, where your function can make use of it. + + The current HTTP Context, such as req.HttpContext. + A task indicating success or failure. In case of failure . + + + + Azure SDK token credential based on the ITokenAcquisition service. + + + + + Constructor from an ITokenAcquisition service. + + Token acquisition. + + + + + + + + + + Factory class to create objects. + + + + + Instantiate a from an account object ID and tenant ID. This can + be useful when the web app subscribes to another service on behalf of the user + and then is called back by a notification where the user is identified by their tenant + ID and object ID (like in Microsoft Graph Web Hooks). + + Tenant ID of the account. + Object ID of the account in this tenant ID. + A containing these two claims. + + + + private async Task GetChangedMessagesAsync(IEnumerable<Notification> notifications) + { + HttpContext.User = ClaimsPrincipalExtension.FromTenantIdAndObjectId(subscription.TenantId, + subscription.UserId); + foreach (var notification in notifications) + { + SubscriptionStore subscription = + subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); + string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); + ...} + } + + + + + + Client assertion. + + + + + Constructor of a ClientAssertion, which can be used instead + of a client secret or client certificates to authenticate the + confidential client application. See https://aka.ms/ms-id-web/client-assertions. + + Signed assertion. + Optional expiry. + + + + Signed assertion. + + + + + Expiry of the client assertion. + + + + + Description of a client assertion in the application configuration. + See https://aka.ms/ms-id-web/client-assertions. + + + + + Constructor of a ClientAssertionDescription. + + delegate providing the client assertion + when it is necessary. + + + + delegate to get the client assertion. + + + + + Client assertion. + + + + + Get the signed assertion (and refreshes it if needed). + + Cancellation token. + The signed assertion. + + + + Expiry of the client assertion. + + Constants for claim types. @@ -169,342 +489,776 @@ Constants related to the log messages. - + - Encoding table. + Extension class containing cookie policies (work around for same site). - + - The following functions perform base64url encoding which differs from regular base64 encoding as follows - * padding is skipped so the pad character '=' doesn't have to be percent encoded - * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') - The changes make the encoding alphabet file and URL safe. + Handles SameSite cookie issue according to the https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1. + The default list of user agents that disallow "SameSite=None", + was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. - string to encode. - Base64Url encoding of the UTF8 bytes. + to update. + to chain. - + - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. + Handles SameSite cookie issue according to the docs: https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1 + The default list of user agents that disallow "SameSite=None", was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. - An array of 8-bit unsigned integers. - The number of elements of inArray to convert. - An offset in inArray. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. + to update. + If you don't want to use the default user agent list implementation, + the method sent in this parameter will be run against the user agent and if returned true, SameSite value will be set to Unspecified. + The default user agent list used can be found at: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + to chain. - + - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. + Checks if the specified user agent supports "SameSite=None" cookies. - An array of 8-bit unsigned integers. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. + Browser user agent. + + Incompatible user agents include: + + Versions of Chrome from Chrome 51 to Chrome 66 (inclusive on both ends). + Versions of UC Browser on Android prior to version 12.13.2. + Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. + + Reference: https://www.chromium.org/updates/same-site/incompatible-clients. + + True, if the user agent does not allow "SameSite=None" cookie; otherwise, false. - + - Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. - base64Url encoded string. - UTF8 bytes. + Implementation for the downstream web API. + - + - Decodes the string from Base64UrlEncoded to UTF8. + Constructor. - string to decode. - UTF8 string. + Token acquisition service. + Named options provider. + HTTP client. + Configuration options. - + + + + + + + + + + - EventIds for Logging. + Merge the options from configuration and override from caller. + Named configuration. + Delegate to override the configuration. - + - Options passed-in to create the token acquisition object which calls into MSAL .NET. + Extension methods to support downstream web API services. - + - Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps - in the cases where a given account is a guest in other tenants, and you want to acquire tokens - for a specific tenant. + Adds a named downstream web API service related to a specific configuration section. - Can be the tenant ID or domain name. + Builder. + Name of the configuration for the service. + This is the name used when calling the service from controller/pages. + Configuration. + The builder for chaining. - + - Uses a particular user flow (in the case of AzureAD B2C) + Adds a named downstream web API service initialized with delegates. + Builder. + Name of the configuration for the service. + This is the name which will be used when calling the service from controller/pages. + Action to configure the options. + The builder for chaining. - + - Sets the correlation id to be used in the authentication request - to the /token endpoint. + Extensions for the downstream web API. - + - Sets Extra Query Parameters for the query string in the HTTP authentication request. + Get a strongly typed response from the web API. + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API with an HttpPost, providing strongly typed input and getting + strongly typed output. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API endpoint with an HttpPut, providing strongly typed input data. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Calls the web API endpoint with an HttpPut, provinding strongly typed input data + and getting back strongly typed data. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Call a web API endpoint with an HttpGet, + and return strongly typed data. + + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Call a web API with a strongly typed input, with an HttpGet. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input data. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. - + - A string with one or multiple claims to request. - Normally used with Conditional Access. + Options passed-in to call downstream web APIs. To call Microsoft Graph, see rather + MicrosoftGraphOptions in the Microsoft.Identity.Web.MicrosoftGraph assembly. - + - Specifies if the token request will ignore the access token in the token cache - and will attempt to acquire a new access token. - If true, the request will ignore the token cache. The default is false. - Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, - for it induces performance degradation, as the token cache is not utilized. + Base URL for the called downstream web API. For instance "https://graph.microsoft.com/beta/".. - + - Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), - rather than a Bearer token. - PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, - which MSAL can manage. See https://aka.ms/msal-net-pop. + Path relative to the (for instance "me"). - + - Cancellation token to be used when calling the token acquisition methods. + HTTP method used to call this downstream web API (by default Get). - + - Key used for long running web APIs that need to call downstream web - APIs on behalf of the user. Can be null, if you are not developing a long - running web API, if you want - Microsoft.Identity.Web to allocate a session key for you, or your own string - if you want to associate the session with some information you have externally - (for instance a Microsoft Graph hook identifier). + Provides an opportunity to customize the HttpRequestMessage. For example, + to customize the headers. This is called after the message was formed, including + the Authorization header, and just before the message is sent. - + - Value that can be used for so that - MSAL.NET allocates the long running web api session key for the developer. + Clone the options (to be able to override them). + A clone of the options. - + + + Return the downstream web API URL. + + URL of the downstream web API. + + Clone the options (to be able to override them). A clone of the options. - + - OpenIdConnect options + Interface used to call a downstream web API, for instance from controllers. - + - Gets or sets the Authority to use when making OpenIdConnect calls. + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. - + - Client secret used to authenticate a confidential client app to AAD - (alternatively use client certificates) + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. - + - Gets or sets the 'client_id' (application ID) as appears in the - application registration. + A DelegatingHandler implementation that add an authorization header with a token for the application. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Initializes a new instance of the class. + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. - + + + + - Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". + Base class for Microsoft Identity authentication message handlers. - + - Gets or sets the tenant ID. + Gets the token acquisition service. - + - Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. + Initializes a new instance of the class. + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. - + - Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. + Gets the options for the specified request. + The request. + The configured options. - + - Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. + Base options passed-in to authenticate with Microsoft Identity. - + - Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. + Space separated scopes required to call the downstream web API. + For instance "user.read mail.read". - + - Gets the default user flow (which is signUpsignIn). + [Optional] tenant ID. This is used for specific scenarios where + the application needs to call a downstream web API on behalf of a user in several tenants. + It would mostly be used from code, not from the configuration. - + - Enables legacy ADAL cache serialization and deserialization. - Performance improvements when working with MSAL only apps. - Set to true if you have a shared cache with ADAL apps. + [Optional]. User flow (in the case of a B2C downstream web API). If not + specified, the B2C downstream web API will be called with the default user flow from + . - The default is false. - + - Is considered B2C if the attribute SignUpSignInPolicyId is defined. + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. + Set to true to enable PoP tokens automatically. - + - Is considered to have client credentials if the attribute ClientCertificates - or ClientSecret is defined. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Description of the certificates used to prove the identity of the web app or web API. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - An example in the appsetting.json: - - "ClientCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - - + - Description of the certificates used to decrypt an encrypted token in a web API. + Returns the scopes. - An example in the appsetting.json: - - "TokenDecryptionCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - + Scopes. - + - Specifies if the x5c claim (public key of the certificate) should be sent to the STS. - Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: - this method will send the public certificate to Azure AD along with the token request, - so that Azure AD can use it to validate the subject name based on a trusted issuer policy. - This saves the application admin from the need to explicitly manage the certificate rollover - (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. + Extension for IHttpClientBuilder for startup initialization of Microsoft Identity authentication handlers. - The default is false. - + - Requests an auth code for the frontend (SPA using MSAL.js for instance). - See https://aka.ms/msal-net/spa-auth-code for details. + Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. - The default is false. + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. - + - Daemon applications can validate a token based on roles, or using the ACL-based authorization - pattern to control tokens without a roles claim. If using ACL-based authorization, - Microsoft Identity Web will not throw if roles or scopes are not in the Claims. - For details see https://aka.ms/ms-identity-web/daemon-ACL. + Adds a named Microsoft Identity user authentication message handler initialized with delegates. - The default is false. + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. - + - Used, when deployed to Azure, to specify explicitly a user assigned managed identity. - See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. + Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. - + - Sets the ResetPassword route path. - Defaults to /MicrosoftIdentity/Account/ResetPassword, - which is the value used by Microsoft.Identity.Web.UI. + Adds a named Microsoft Identity application authentication message handler initialized with delegates. + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. - + - Sets the Error route path. - Defaults to the value /MicrosoftIdentity/Account/Error, - which is the value used by Microsoft.Identity.Web.UI. + Options passed-in to Microsoft Identity message handlers. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. + Clone the options (to be able to override them). + A clone of the options. - + - Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). + Clone the options (to be able to override them). + + A clone of the options. + + + + A DelegatingHandler implementation that add an authorization header with a token on behalf of the current user. - + - In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user - signed-in and consented) - the token redeemed from the "/> is added to the cache, so that it can then be used to acquire another token on-behalf-of the - same user in order to call to downstream APIs. + Initializes a new instance of the class. - Scopes to request. Can be empty - Authorization code - Authentication scheme to use (config section) - Client Info obtained with the code - PKCE code verifier - User flow in the case of B2C - The ID Token. + Token acquisition service. + Named options provider. + Configuration options. + Name of the service describing the downstream web API. - + + + + - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Extension methods. - Signed in user + + + Determines whether the specified string collection contains any. + The search for. + The string collection. + + true if the specified string collection contains any; otherwise, false. + + + + Keep the validated token associated with the HTTP request. + + HTTP context. + Token to preserve after the token is validated so that + it can be used in the actions. + + + + Get the parsed information about the token used to call the web API. + + HTTP context associated with the current request. + used to call the web API. + + + + Provides access to get or set the current error status. + The default implementation will use TempData and be enabled when run under Development. + + + + + Gets the error message for the current request. + + Current . + The current error message if available. + + + + Sets the error message for the current request. + + Current . + Error message to set. + + + + Gets whether error messages should be displayed. + + + + + Helper methods to handle incremental consent and conditional access in + a web app. + + + + + Can the exception be solved by re-signing-in the user?. + + Exception from which the decision will be made. + Returns true if the issue can be solved by signing-in + the user, and false, otherwise. + + + + Build authentication properties needed for incremental consent. + + Scopes to request. + instance. + User. + Userflow being invoked for AAD B2C. + AuthenticationProperties. + + + + An implementation of IConfigurationRetriever geared towards Azure AD issuers metadata. + + + + Retrieves a populated configuration given an address and an . + Address of the discovery document. + The to use to read the discovery document. + A cancellation token that can be used by other objects or threads to receive notice of cancellation. . + + A that, when completed, returns from the configuration. + + address - Azure AD Issuer metadata address URL is required + or retriever - No metadata document retriever is provided. + + + + Model class to hold information parsed from the Azure AD issuer endpoint. + + + + + Issuer associated with the OIDC endpoint. + + + + + Interface for the token acquisition service (encapsulating MSAL.NET). + + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Options passed-in to create the token acquisition object which calls into MSAL .NET. Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - A that represents a completed remove from cache operation. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + An to call on behalf of the user, the downstream API characterized by its scopes. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Options passed-in to create the token acquisition object which calls into MSAL .NET. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + An authentication result for the app itself, based on its scopes. Interface for the token acquisition service (encapsulating MSAL.NET). + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call on behalf of the user, the downstream API characterized by its scopes. + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -522,6 +1276,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token to call on behalf of the user, the downstream API characterized by its scopes. + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the current user + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An to call on behalf of the user, the downstream API characterized by its scopes. + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -539,6 +1308,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An to call on behalf of the user, the downstream API characterized by its scopes. + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -556,6 +1340,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token for the app itself, based on its scopes. + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -573,291 +1372,1672 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An authentication result for the app itself, based on its scopes. - + - Get the effective authentication scheme based on the context. + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. - Proposed authentication scheme. - Effective authenticationScheme which is the authentication scheme - if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme + Scopes to consent to. + triggering the challenge. + The to update. + + + + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + + Scopes to consent to. + triggering the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + Get the effective authentication scheme based on the context. + + Proposed authentication scheme. + Effective authenticationScheme which is the authentication scheme + if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - + - Token acquisition service. + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + Scopes to consent to. + triggering the challenge. + The to update. + A representing the asynchronous operation. + + - LoggingMessage class for TokenAcquisition. + Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). - + + + In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user + signed-in and consented) + An On-behalf-of token contained in the is added to the cache, so that it can then be used to acquire another token on-behalf-of the + same user in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + Scopes to request. + Authentication scheme to use. + A that represents a completed add to cache operation. + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + + + - Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Signed in user + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + A that represents a completed remove from cache operation. - + - Scopes which are already requested by MSAL.NET. They should not be re-requested;. + EventIds for Logging. - + - Meta-tenant identifiers which are not allowed in client credentials. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. - + - Constructor of the TokenAcquisition service. This requires the Azure AD Options to - configure the confidential client application and a token cache provider. - This constructor is called by ASP.NET Core dependency injection. + Base class for web app and web API Microsoft Identity authentication + builders. - The App token cache provider. - Host of the token acquisition. - HTTP client factory. - Logger. - Service provider. - + - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from claims provided in the current claims principal. - instance of the current HttpContext. + Constructor. - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Options passed-in to create the token acquisition options object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + The services being configured. + Optional configuration section. - + - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + The services being configured. - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An authentication result for the app itself, based on its scopes. - + - Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + Configuration section from which to bind options. - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token for the app itself, based on its scopes. + It can be null if the configuration happens with delegates + rather than configuration. - + - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. - instance of the current HttpContext. + Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - + - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Add the incremental consent and conditional access handler for Blazor + server side pages. - User - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A that represents a completed account removal operation. + Service side blazor builder. + The builder. - + - Creates an MSAL confidential client application. + Add the incremental consent and conditional access handler for + web app pages, Razor pages, controllers, views, etc... + Service collection. + The service collection. - + - Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + Handler for Blazor specific APIs to handle incremental consent + and conditional access. - . - Claims principal for the user on behalf of whom to get a token. - Scopes for the downstream API to call. - (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user described in the claimsPrincipal. - Merged options. - Azure AD B2C user flow to target. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. + Initializes a new instance of the class. - . - User IAccount for which to acquire a token. - See . - Scopes for the downstream API to call. - TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user. - Merged options. - Azure AD B2C user flow. - Options passed-in to create the token acquisition object which calls into MSAL .NET. + Service provider to get the HttpContextAccessor for the current HttpContext, when available. - + - Logger for handling MSAL exceptions in TokenAcquisition. + Boolean to determine if server is Blazor. - ILogger. - Specific log message from TokenAcquisition. - Exception from MSAL.NET. - + - Logger for handling information specific to MSAL in token acquisition. + Current user. - ILogger. - durationTotalInMs. - durationInHttpInMs. - durationInCacheInMs. - cache or IDP. - correlationId. - cacheRefreshReason. - Exception from MSAL.NET. - + - Prototype of certificate-less authentication using a signed assertion - acquired with MSI (federated identity). + Base URI to use in forming the redirect. - The signed assertion. - + - Client assertion. + For Blazor/Razor pages to process the exception from + a user challenge. + Exception. - + - Constructor of a ClientAssertion, which can be used instead - of a client secret or client certificates to authenticate the - confidential client application. See https://aka.ms/ms-id-web/client-assertions. + Forces the user to consent to specific scopes and perform + Conditional Access to get specific claims. Use on a Razor/Blazor + page or controller to proactively ensure the scopes and/or claims + before acquiring a token. The other mechanism + ensures claims and scopes requested by Azure AD after a failed token acquisition attempt. + See https://aka.ms/ms-id-web/ca_incremental-consent for details. - Signed assertion. - Optional expiry. + Scopes to request. + Claims to ensure. + Userflow being invoked for AAD B2C. - + - Signed assertion. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - + - Expiry of the client assertion. + Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". - + - Description of a client assertion in the application configuration. - See https://aka.ms/ms-id-web/client-assertions. + Gets or sets the tenant ID. - + - Constructor of a ClientAssertionDescription. + Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. - delegate providing the client assertion - when it is necessary. - + - delegate to get the client assertion. + Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. - + - Client assertion. + Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. - + - Get the signed assertion (and refreshes it if needed). + Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. - Cancellation token. - The signed assertion. - + - Expiry of the client assertion. + Gets the default user flow (which is signUpsignIn). - + - Microsoft Identity Web specific exception class for - use in Blazor or Razor pages to process the user challenge. - Handles the . + Enables legacy ADAL cache serialization and deserialization. + Performance improvements when working with MSAL only apps. + Set to true if you have a shared cache with ADAL apps. + The default is false. - + - Exception thrown by MSAL when a user challenge is encountered. + Is considered B2C if the attribute SignUpSignInPolicyId is defined. - + - Scopes to request. + Is considered to have client credentials if the attribute ClientCertificates + or ClientSecret is defined. - + - Specified userflow. + Description of the certificates used to prove the identity of the web app or web API. + An example in the appsetting.json: + + "ClientCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + - + - Handles the user challenge for Blazor or Razor pages. + Description of the certificates used to decrypt an encrypted token in a web API. - Exception thrown by MSAL when a user challenge is encountered. - Scopes to request. - Userflow used in B2C. + An example in the appsetting.json: + + "TokenDecryptionCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + + + + + Specifies if the x5c claim (public key of the certificate) should be sent to the STS. + Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: + this method will send the public certificate to Azure AD along with the token request, + so that Azure AD can use it to validate the subject name based on a trusted issuer policy. + This saves the application admin from the need to explicitly manage the certificate rollover + (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. + + The default is false. + + + + Requests an auth code for the frontend (SPA using MSAL.js for instance). + See https://aka.ms/msal-net/spa-auth-code for details. + + The default is false. + + + + Daemon applications can validate a token based on roles, or using the ACL-based authorization + pattern to control tokens without a roles claim. If using ACL-based authorization, + Microsoft Identity Web will not throw if roles or scopes are not in the Claims. + For details see https://aka.ms/ms-identity-web/daemon-ACL. + + The default is false. + + + + Used, when deployed to Azure, to specify explicitly a user assigned managed identity. + See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. + + + + + Sets the ResetPassword route path. + Defaults to /MicrosoftIdentity/Account/ResetPassword, + which is the value used by Microsoft.Identity.Web.UI. + + + + + Sets the Error route path. + Defaults to the value /MicrosoftIdentity/Account/Error, + which is the value used by Microsoft.Identity.Web.UI. + + + + + Microsoft Identity Web specific exception class for + use in Blazor or Razor pages to process the user challenge. + Handles the . + + + + + Exception thrown by MSAL when a user challenge is encountered. + + + + + Scopes to request. + + + + + Specified userflow. + + + + + Handles the user challenge for Blazor or Razor pages. + + Exception thrown by MSAL when a user challenge is encountered. + Scopes to request. + Userflow used in B2C. + + + + Prototype of certificate-less authentication using a signed assertion + acquired with MSI (federated identity). + + The signed assertion. + + + + This is the metadata that describes required auth scopes for a given endpoint + in a web API. It's the underlying data structure the requirement will look for + in order to validate scopes in the scope claims. + + + + + Scopes accepted by this web API. + + + + + Fully qualified name of the configuration key containing the required scopes (separated + by spaces). + + + + + Extensions for building the RequiredScope policy during application startup. + + + + services.AddAuthorization(o => + { o.AddPolicy("Custom", + policyBuilder =>policyBuilder.RequireScope("access_as_user")); + }); + + + + + + Adds a to the current instance which requires + that the current user has the specified claim and that the claim value must be one of the allowed values. + + Used for building policies during application startup. + Values the claim must process one or more of for evaluation to succeed. + A reference to this instance after the operation has completed. + + + + Adds a to the current instance which requires + that the current user has the specified claim and that the claim value must be one of the allowed values. + + Used for building policies during application startup. + Values the claim must process one or more of for evaluation to succeed. + A reference to this instance after the operation has completed. + + + + This attribute is used on a controller, pages, or controller actions + to declare (and validate) the scopes required by a web API. These scopes can be declared + in two ways: hardcoding them, or declaring them in the configuration. Depending on your + choice, use either one or the other of the constructors. + For details, see https://aka.ms/ms-id-web/required-scope-attribute. + + + + + Scopes accepted by this web API. + + + + + Fully qualified name of the configuration key containing the required scopes (separated + by spaces). + + + If the appsettings.json file contains a section named "AzureAd", in which + a property named "Scopes" contains the required scopes, the attribute on the + controller/page/action to protect should be set to the following: + + [RequiredScope(RequiredScopesConfigurationKey="AzureAd:Scopes")] + + + + + + Verifies that the web API is called with the right scopes. + If the token obtained for this API is on behalf of the authenticated user does not have + any of these in its scope claim, the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + + Scopes accepted by this web API. + When the scopes don't match, the response is a 403 (Forbidden), + because the user is authenticated (hence not 401), but not authorized. + + Add the following attribute on the controller/page/action to protect: + + + [RequiredScope("access_as_user")] + + + and + if you want to express the required scopes from the configuration. + + + + Default constructor. + + + + [RequiredScope(RequiredScopesConfigurationKey="AzureAD:Scope")] + class Controller : BaseController + { + } + + + + + + Unused: Compatibility of interface with the Authorization Filter. + + + + + Interface implemented by diagnostics for the JWT Bearer middleware. + + + + + Called to subscribe to . + + JWT Bearer events. + The events (for chaining). + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Method to subscribe to . + + OpenID Connect events. + + + + Diagnostics for the JwtBearer middleware (used in web APIs). + + + + + Constructor for a . This constructor + is used by dependency injection. + + Logger. + + + + Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after the security token has passed validation and a ClaimsIdentity has been generated. + + + + + Invoked before a challenge is sent back to the caller. + + + + + Subscribes to all the JwtBearer events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + for chaining. + + + + Factory class for creating the IssuerValidator per authority. + + + + + Initializes a new instance of the class. + + Options passed-in to create the AadIssuerValidator object. + HttpClientFactory. + + + + Gets an for an authority. + + The authority to create the validator for, e.g. https://login.microsoftonline.com/. + A for the aadAuthority. + if is null or empty. + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Constructor of the , used + by dependency injection. + + Logger used to log the diagnostics. + + + + Invoked before redirecting to the identity provider to authenticate. This can + be used to set ProtocolMessage.State that will be persisted through the authentication + process. The ProtocolMessage can also be used to add or customize parameters + sent to the identity provider. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after security token validation if an authorization code is present + in the protocol message. + + + + + Invoked after "authorization code" is redeemed for tokens at the token endpoint. + + + + + Invoked when an IdToken has been validated and produced an AuthenticationTicket. + + + + + Invoked when user information is retrieved from the UserInfoEndpoint. + + + + + Invoked if exceptions are thrown during request processing. The exceptions will + be re-thrown after this event unless suppressed. + + + + + Invoked when a request is received on the RemoteSignOutPath. + + + + + Invoked before redirecting to the identity provider to sign out. + + + + + Invoked before redirecting to the Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutRedirectUri + at the end of a remote sign-out flow. + + + + + Subscribes to all the OpenIdConnect events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + + + + Generic class that registers the token audience from the provided Azure AD authority. + + + + + Default validation of the audience: + - when registering an Azure AD web API in the app registration portal (and adding a scope) + the default App ID URI generated by the portal is api://{clientID} + - However, the audience (aud) of the token acquired to access this web API is different depending + on the "accepted access token version" for the web API: + - if accepted token version is 1.0, the audience provided in the token + by the Microsoft identity platform (formerly Azure AD v2.0) endpoint is: api://{ClientID} + - if the accepted token version is 2.0, the audience provided by Azure AD v2.0 in the token + is {CliendID} + When getting an access token for an Azure AD B2C web API the audience in the token is + api://{ClientID}. + + When web API developers don't provide the "Audience" in the configuration, Microsoft.Identity.Web + considers that this is the default App ID URI as explained above. When developer provides the + "Audience" member, it's available in the TokenValidationParameter.ValidAudience. + + Audiences in the security token. + Security token from which to validate the audiences. + Token validation parameters. + True if the token is valid; false, otherwise. + + + + Extension class providing the extension methods for that + can be used in web APIs to validate the roles in controller actions. + + + + + When applied to an , verifies that the application + has the expected roles. + + HttpContext (from the controller). + Roles accepted by this web API. + When the roles don't match, the response is a 403 (Forbidden), + because the app does not have the expected roles. + + + + Extension class providing the extension + methods for that + can be used in web APIs to validate scopes in controller actions. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. + + + + + When applied to an , verifies that the user authenticated in the + web API has any of the accepted scopes. + If there is no authenticated user, the response is a 401 (Unauthenticated). + If the authenticated user does not have any of these , the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. + + HttpContext (from the controller). + Scopes accepted by this web API. + + + + Extensions for building the required scope attribute during application startup. + + + + + This method adds support for the required scope attribute. It adds a default policy that + adds a scope requirement. This requirement looks for IAuthRequiredScopeMetadata on the current endpoint. + + The services being configured. + Services. + + + + This method adds metadata to route endpoint to describe required scopes. It's the imperative version of + the [RequiredScope] attribute. + + Class implementing . + To customize the endpoints. + Scope. + Builder. + + + + RequireScopeOptions. + + + + + Sets the default policy. + + + + + + + + Scope authorization handler that needs to be called for a specific requirement type. + In this case, . + + + + + Constructor for the scope authorization handler, which takes a configuration. + + Configuration. + + + + Makes a decision if authorization is allowed based on a specific requirement. + + AuthorizationHandlerContext. + Scope authorization requirement. + Task. + + + + Implements an + which requires at least one instance of the specified claim type, and, if allowed values are specified, + the claim value must be any of the allowed values. + + + + + Creates a new instance of . + + The optional list of scope values. + + + + Gets the optional list of scope values. + + + + + Gets the optional list of scope values from configuration. + + + + + + + + Options passed-in to create the AadIssuerValidator object. + + + + + Sets the name of the HttpClient to get from the IHttpClientFactory for use with the configuration manager. + Needed when customizing the client such as configuring a proxy. + + + + + Extensions for IServiceCollection for startup initialization of web APIs. + + + + + Add the token acquisition service. + + Service collection. + Specifies if an instance of should be a singleton. + The service collection. + + This method is typically called from the ConfigureServices(IServiceCollection services) in Startup.cs. + Note that the implementation of the token cache can be chosen separately. + + + // Token acquisition service and its cache implementation as a session cache + services.AddTokenAcquisition() + .AddDistributedMemoryCache() + .AddSession() + .AddSessionBasedTokenCache(); + + + + + + An implementation of that uses to track error messages. + + + + + Token acquisition service. + + + LoggingMessage class for TokenAcquisition. + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + The App token cache provider. + Host of the token acquisition service. + HTTP client factory. + Logger. + Service provider. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + The to update. + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + This handler is executed after the authorization code is received (once the user signs-in and consents) during the + authorization code flow in a web app. + It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. + The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the + on-behalf-of flow for the signed-in user's account, + in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + scopes to request access to. + Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + + + + + Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. + + + + + Scopes which are already requested by MSAL.NET. They should not be re-requested;. + + + + + Meta-tenant identifiers which are not allowed in client credentials. + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + The App token cache provider. + Host of the token acquisition. + HTTP client factory. + Logger. + Service provider. + + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from claims provided in the current claims principal. + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Options passed-in to create the token acquisition options object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + + User + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A that represents a completed account removal operation. + + + + Creates an MSAL confidential client application. + + + + + Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + + . + Claims principal for the user on behalf of whom to get a token. + Scopes for the downstream API to call. + (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user described in the claimsPrincipal. + Merged options. + Azure AD B2C user flow to target. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. + + . + User IAccount for which to acquire a token. + See . + Scopes for the downstream API to call. + TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user. + Merged options. + Azure AD B2C user flow. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + Logger for handling MSAL exceptions in TokenAcquisition. + + ILogger. + Specific log message from TokenAcquisition. + Exception from MSAL.NET. + + + + Logger for handling information specific to MSAL in token acquisition. + + ILogger. + durationTotalInMs. + durationInHttpInMs. + durationInCacheInMs. + cache or IDP. + correlationId. + cacheRefreshReason. + Exception from MSAL.NET. + + + + Implementation of ITokenAcquisitionHost in the case of ASP.NET Core + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + Access to the HttpContext of the request. + Configuration options. + Logger. + Service provider. + + + + + + + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + + Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps + in the cases where a given account is a guest in other tenants, and you want to acquire tokens + for a specific tenant. + + Can be the tenant ID or domain name. + + + + Uses a particular user flow (in the case of AzureAD B2C) + + + + + Sets the correlation id to be used in the authentication request + to the /token endpoint. + + + + + Sets Extra Query Parameters for the query string in the HTTP authentication request. + + + + + A string with one or multiple claims to request. + Normally used with Conditional Access. + + + + + Specifies if the token request will ignore the access token in the token cache + and will attempt to acquire a new access token. + If true, the request will ignore the token cache. The default is false. + Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, + for it induces performance degradation, as the token cache is not utilized. + + + + + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. + + + + + Cancellation token to be used when calling the token acquisition methods. + + + + + Key used for long running web APIs that need to call downstream web + APIs on behalf of the user. Can be null, if you are not developing a long + running web API, if you want + Microsoft.Identity.Web to allocate a session key for you, or your own string + if you want to associate the session with some information you have externally + (for instance a Microsoft Graph hook identifier). + + + + + Value that can be used for so that + MSAL.NET allocates the long running web api session key for the developer. + + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + An implementation of token cache for confidential clients backed by an HTTP session. + + + For this session cache to work effectively, the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + + https://aka.ms/msal-net-token-cache-serialization + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + MSAL Token cache provider constructor. + + Session for the current user. + Logger. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Read blob. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Hints for the cache serialization implementation optimization. + Read blob. + + + + Writes the token cache identified by its key to the serialization mechanism. + + Key for the cache (account ID or app ID). + Blob to write to the cache. + A that completes when a write operation has completed. + + + + Removes a cache described by its key. + + Key of the token cache (user account or app ID). + A that completes when key removal has completed. + + + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + Session cache logging. + + ILogger. + /// Cache operation (Read, Write, etc...). + Session Id. + MSAL.NET cache key. + Exception. + + + + Session cache deserialized. + + ILogger. + MSAL.NET cache key. + Session Id. + Exception. + + + + Extension class to add a session token cache serializer to MSAL. + + + + + Adds an HTTP session-based application token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + + + + Adds an HTTP session-based per-user token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + + + + Encoding table. + + + + + The following functions perform base64url encoding which differs from regular base64 encoding as follows + * padding is skipped so the pad character '=' doesn't have to be percent encoded + * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') + The changes make the encoding alphabet file and URL safe. + + string to encode. + Base64Url encoding of the UTF8 bytes. + + + + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. + + An array of 8-bit unsigned integers. + The number of elements of inArray to convert. + An offset in inArray. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. + + + + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. + + An array of 8-bit unsigned integers. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. + + + + Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. + base64Url encoded string. + UTF8 bytes. + + + + Decodes the string from Base64UrlEncoded to UTF8. + + string to decode. + UTF8 string. + + + + Authentication builder for a web API. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + ACtion called to configure the JwtBearer options. + Action called to configure + the Microsoft identity options. + Configuration section from which to + get parameters. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + + The action to configure . + The authentication builder to chain. + + + + Extensions for for startup initialization of web APIs. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration second from which to fill-in the options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + + The to which to add this configuration. + The action to configure . + The action to configure the . + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + The authentication builder to chain. + + + + Builder for web API authentication with configuration. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The authentication builder to chain. + + + + Extension for IServiceCollection for startup initialization of web APIs. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0) + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + Service collection to which to add authentication. + The Configuration object. + The configuration section with the necessary settings to initialize authentication options. + The JwtBearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JwtBearer events. + The authentication builder to chain extension methods. + + + + Web API authentication builder. + + + + + Allows a higher level abstraction of security token (i.e. System.IdentityModel.Tokens.Jwt and more modern, Microsoft.IdentityModel.JsonWebTokens) + to be used with Microsoft Identity Web. + Developers should continue to use `EnableTokenAcquisitionToCallDownstreamApi`. + This API is not considered part of the public API and may change. + + The action to configure . + Authentication scheme. + The services being configured. + IConfigurationSection. + The authentication builder to chain. + + + + Authentication builder returned by the EnableTokenAcquisitionToCallDownstreamApi methods + enabling you to decide token cache implementations. + + + + + Add in memory token caches. + + to configure. + to configure. + the service collection. + + + + Add distributed token caches. + + the service collection. + + + + Add session token caches. + + the service collection. + + + + Authentication builder specific for Microsoft identity platform. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + The web app calls a web API. + + Initial scopes. + The builder itself for chaining. + + + + The web app calls a web API. This override enables you to specify the + ConfidentialClientApplicationOptions (from MSAL.NET) programmatically. + + Action to configure the + MSAL.NET confidential client application options. + Initial scopes. + The builder itself for chaining. + + + + Extensions for the for startup initialization. + + + + + Add authentication to a web app with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, + with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The builder for chaining. + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration section from which to get the options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + Configuration section. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Builder for a Microsoft identity web app authentication where configuration is + available for EnableTokenAcquisitionToCallDownstreamApi. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + Add support for the web app to acquire tokens to call an API. + + Optional initial scopes to request. + The authentication builder for chaining. + + + + Extension for IServiceCollection for startup initialization. + + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, (by default named "AzureAd"), with the necessary settings to + initialize the authentication options. + + Service collection to which to add authentication. + The IConfiguration object. + The name of the configuration section with the necessary + settings to initialize authentication options. + Optional name for the open id connect authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). This can be specified when you want to support + several OpenIdConnect identity providers. + Optional name for the cookie authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). + Set to true if you want to debug, or just understand the OpenIdConnect events. + A display name for the authentication handler. + The authentication builder to chain extension methods. diff --git a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs index 187e1b0e0..7c1ec6417 100644 --- a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs +++ b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs @@ -59,7 +59,7 @@ public class MicrosoftIdentityOptions : OpenIdConnectOptions /// /// Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". /// - public string Instance { get; set; } = null!; + public string Instance { get; set; } = "https://login.microsoftonline.com/"; /// /// Gets or sets the tenant ID. diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index e39b20e75..fe3cbe13e 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Identity.Web.Hosts; namespace Microsoft.Identity.Web { @@ -41,6 +42,7 @@ public static IServiceCollection AddTokenAcquisition( ServiceDescriptor? tokenAcquisitionService = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisition)); ServiceDescriptor? tokenAcquisitionInternalService = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisitionInternal)); + ServiceDescriptor? tokenAcquisitionhost = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisitionHost)); if (tokenAcquisitionService != null && tokenAcquisitionInternalService != null) { if (isTokenAcquisitionSingleton ^ (tokenAcquisitionService.Lifetime == ServiceLifetime.Singleton)) @@ -48,6 +50,7 @@ public static IServiceCollection AddTokenAcquisition( // The service was already added, but not with the right lifetime services.Remove(tokenAcquisitionService); services.Remove(tokenAcquisitionInternalService); + services.Remove(tokenAcquisitionhost); } else { @@ -57,18 +60,27 @@ public static IServiceCollection AddTokenAcquisition( } // Token acquisition service - services.AddHttpContextAccessor(); if (isTokenAcquisitionSingleton) { services.AddSingleton(); +#if !NET472 && !NET462 + services.AddHttpContextAccessor(); services.AddSingleton(); services.AddSingleton(s => (ITokenAcquisitionInternal)s.GetRequiredService()); +#else + services.AddSingleton(); +#endif } else { services.AddScoped(); +#if !NET472 && !NET462 + services.AddHttpContextAccessor(); services.AddScoped(); services.AddScoped(s => (ITokenAcquisitionInternal)s.GetRequiredService()); +#else + services.AddScoped(); +#endif } return services; diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs index 82bff3837..8ec87e7f6 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs @@ -89,6 +89,8 @@ public TokenAcquisitionOptions Clone() { return new TokenAcquisitionOptions { + Tenant = Tenant, + UserFlow = UserFlow, CorrelationId = CorrelationId, ExtraQueryParameters = ExtraQueryParameters, ForceRefresh = ForceRefresh, diff --git a/tests/daemon-app/Daemon-app/Daemon-app.csproj b/tests/daemon-app/Daemon-app/Daemon-app.csproj new file mode 100644 index 000000000..505a16154 --- /dev/null +++ b/tests/daemon-app/Daemon-app/Daemon-app.csproj @@ -0,0 +1,27 @@ + + + + Exe + net462 + Daemon_app + enable + enable + 10.0 + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/tests/daemon-app/Daemon-app/Program.cs b/tests/daemon-app/Daemon-app/Program.cs new file mode 100644 index 000000000..c1ad4b8b2 --- /dev/null +++ b/tests/daemon-app/Daemon-app/Program.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Graph; +using Microsoft.Identity.Web; +using Microsoft.Identity.Web.TokenCacheProviders.Distributed; + +namespace daemon_console +{ + /// + /// This sample shows how to query the Microsoft Graph from a daemon application + /// which uses application permissions. + /// For more information see https://aka.ms/msal-net-client-credentials + /// + class Program + { + static void Main(string[] args) + { + try + { + RunAsync().GetAwaiter().GetResult(); + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(ex.Message); + Console.ResetColor(); + } + + Console.WriteLine("Press any key to exit"); + Console.ReadKey(); + } + + private static async Task RunAsync() + { + // Read the configuration from a file + IConfiguration Configuration; + var builder = new ConfigurationBuilder() + .SetBasePath(System.IO.Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + Configuration = builder.Build(); + + // Add services + ServiceCollection services = new ServiceCollection(); + + services.Configure(option => Configuration.Bind(option)); + services.AddMicrosoftGraph(); + services.AddDistributedTokenCaches(); + services.AddDistributedMemoryCache(); /* or SQL, Redis, ... */ + + var serviceProvider = services.BuildServiceProvider(); + + + + +#if WithTokenAcquisition + AuthenticationResult result = null; + // Get the token acquisition service + ITokenAcquisition? tokenAcquisition = serviceProvider.GetService(); + string scope = Configuration.GetValue("Scopes"); + result = await tokenAcquisition.GetAuthenticationResultForAppAsync(scope, null); +#else + GraphServiceClient graphServiceClient = serviceProvider.GetRequiredService(); + var users = await graphServiceClient.Users + .Request() + .WithAppOnly() + .GetAsync(); + + Console.WriteLine($"{users.Count} users"); +#endif + } + } +} diff --git a/tests/daemon-app/Daemon-app/appsettings.json b/tests/daemon-app/Daemon-app/appsettings.json new file mode 100644 index 000000000..12d277886 --- /dev/null +++ b/tests/daemon-app/Daemon-app/appsettings.json @@ -0,0 +1,12 @@ +{ + "Instance": "https://login.microsoftonline.com/", + "TenantId": "msidentitysamplestesting.onmicrosoft.com", + "ClientId": "6af093f3-b445-4b7a-beae-046864468ad6", + "ClientCertificates": [ + { + "SourceType": "KeyVault", + "KeyVaultUrl": "https://webappsapistests.vault.azure.net", + "KeyVaultCertificateName": "Self-Signed-5-5-22" + } + ] +} From 343d12d63e977787297ca86f26ab9496af237eba Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Wed, 16 Mar 2022 21:56:21 -0700 Subject: [PATCH 04/48] - Adding a plain dotnet token acquisition host. - AddTokenAcquisition now callable from net462 - AddGraph calllable from net462 - Adding a daemon sample using Id.Web. --- Microsoft.Identity.Web.sln | 14 +- .../GraphServiceCollectionExtensions.cs | 54 + ...crosoft.Identity.Web.MicrosoftGraph.csproj | 3 +- .../MicrosoftGraphExtensions.cs | 32 +- ...oft.Identity.Web.MicrosoftGraphBeta.csproj | 1 + .../PlainDotNetTokenAcquisitionHost.cs | 75 + src/Microsoft.Identity.Web/ITokenAcquirer.cs | 13 +- .../Microsoft.Identity.Web.csproj | 1 + .../Microsoft.Identity.Web.xml | 2934 ++++++++++++++--- .../MicrosoftIdentityOptions.cs | 2 +- .../ServiceCollectionExtensions.cs | 14 +- .../TokenAcquisitionOptions.cs | 2 + tests/daemon-app/Daemon-app/Daemon-app.csproj | 27 + tests/daemon-app/Daemon-app/Program.cs | 77 + tests/daemon-app/Daemon-app/appsettings.json | 12 + 15 files changed, 2845 insertions(+), 416 deletions(-) create mode 100644 src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs create mode 100644 src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs create mode 100644 tests/daemon-app/Daemon-app/Daemon-app.csproj create mode 100644 tests/daemon-app/Daemon-app/Program.cs create mode 100644 tests/daemon-app/Daemon-app/appsettings.json diff --git a/Microsoft.Identity.Web.sln b/Microsoft.Identity.Web.sln index 7c8456894..3905f2541 100644 --- a/Microsoft.Identity.Web.sln +++ b/Microsoft.Identity.Web.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29009.5 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web", "src\Microsoft.Identity.Web\Microsoft.Identity.Web.csproj", "{FD55C071-48D1-4FE8-8B1D-773E067FEC91}" EndProject @@ -134,6 +134,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Toke EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Certificate", "src\Microsoft.Identity.Web.Certificate\Microsoft.Identity.Web.Certificate.csproj", "{1E0B96CD-FDBF-482C-996A-775F691D984E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Daemon", "Daemon", "{5D970909-98CF-415C-A598-D60B4B4AD8A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Daemon-app", "tests\daemon-app\Daemon-app\Daemon-app.csproj", "{8C557C25-25C9-45B6-B2D3-77093893EBC5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -300,6 +304,10 @@ Global {1E0B96CD-FDBF-482C-996A-775F691D984E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E0B96CD-FDBF-482C-996A-775F691D984E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E0B96CD-FDBF-482C-996A-775F691D984E}.Release|Any CPU.Build.0 = Release|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -354,6 +362,8 @@ Global {D3A42D78-8A23-44EB-9A1F-63CF58B56AAC} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} {15B7F1B3-A438-4A42-BFFA-E3382543C037} = {D3A42D78-8A23-44EB-9A1F-63CF58B56AAC} {5B9AD363-6A27-4AF2-939E-B20E7C941ADF} = {20CDB9B2-D237-413A-8E76-A973597748B0} + {5D970909-98CF-415C-A598-D60B4B4AD8A9} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} + {8C557C25-25C9-45B6-B2D3-77093893EBC5} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4FA8C4C-3251-41CC-939B-7892F5798549} diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs new file mode 100644 index 000000000..1103bbc86 --- /dev/null +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/GraphServiceCollectionExtensions.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Graph; + +namespace Microsoft.Identity.Web +{ + public static class GraphServiceCollectionExtensions + { + public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services) + { + services.AddTokenAcquisition(); + services.AddHttpClient(); + return services.AddMicrosoftGraph(options => { }); + } + + public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, Action configureMicrosoftGraphOptions) + { + // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests + services.AddOptions().Configure(configureMicrosoftGraphOptions); + + services.AddScoped(serviceProvider => + { + var tokenAquisitionService = serviceProvider.GetRequiredService(); + var options = serviceProvider.GetRequiredService>(); + + var microsoftGraphOptions = options.Value; + if (microsoftGraphOptions.Scopes == null) + { + throw new ArgumentException(IDWebErrorMessage.CalledApiScopesAreNull); + } + + string graphBaseUrl = microsoftGraphOptions.BaseUrl; + string[] initialScopes = microsoftGraphOptions.Scopes.Split(' '); + + GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? + new GraphServiceClient(new TokenAcquisitionAuthenticationProvider( + tokenAquisitionService, + new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })) : + new GraphServiceClient(graphBaseUrl, + new TokenAcquisitionAuthenticationProvider( + tokenAquisitionService, + new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })); + return client; + }); + return services; + } + } + +} diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj index a91fed1ec..726ae026c 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj @@ -62,11 +62,12 @@ - netcoreapp3.1; net5.0 + netcoreapp3.1; net5.0; net462; net472 true ../../build/MSAL.snk true enable + 10.0 diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs b/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs index 56c12608c..299cb8eac 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/MicrosoftGraphExtensions.cs @@ -11,6 +11,7 @@ namespace Microsoft.Identity.Web { +#if !NET472 && !NET462 /// /// Extensions methods on a MicrosoftIdentityAppCallingWebApiAuthenticationBuilder builder /// to add support to call Microsoft Graph. @@ -66,36 +67,12 @@ public static MicrosoftIdentityAppCallsWebApiAuthenticationBuilder AddMicrosoftG throw new ArgumentNullException(nameof(builder)); } - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - builder.Services.AddOptions().Configure(configureMicrosoftGraphOptions); - - builder.Services.AddScoped(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetRequiredService(); - var options = serviceProvider.GetRequiredService>(); - - var microsoftGraphOptions = options.Value; - if (microsoftGraphOptions.Scopes == null) - { - throw new ArgumentException(IDWebErrorMessage.CalledApiScopesAreNull); - } - - string graphBaseUrl = microsoftGraphOptions.BaseUrl; - string[] initialScopes = microsoftGraphOptions.Scopes.Split(' '); - - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionAuthenticationProvider( - tokenAquisitionService, - new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })) : - new GraphServiceClient(graphBaseUrl, - new TokenAcquisitionAuthenticationProvider( - tokenAquisitionService, - new TokenAcquisitionAuthenticationProviderOption() { Scopes = initialScopes.ToArray() })); - return client; - }); + builder.Services.AddMicrosoftGraph(configureMicrosoftGraphOptions); return builder; } + + /// /// Add support to call Microsoft Graph. /// @@ -150,4 +127,5 @@ public static MicrosoftIdentityAppCallsWebApiAuthenticationBuilder AddMicrosoftG return builder; } } +#endif } diff --git a/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj b/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj index 94aa7ca4a..270493b7b 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj +++ b/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs b/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs new file mode 100644 index 000000000..ddfa88ee1 --- /dev/null +++ b/src/Microsoft.Identity.Web/Hosts/PlainDotNet/PlainDotNetTokenAcquisitionHost.cs @@ -0,0 +1,75 @@ +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Microsoft.Identity.Client; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.Identity.Web.Hosts +{ + internal class PlainDotNetTokenAcquisitionHost : ITokenAcquisitionHost + { + + IOptionsMonitor _optionsMonitor; + IOptionsMonitor _mergedOptionsMonitor; + IOptionsMonitor _ccaOptionsMonitor; + + public PlainDotNetTokenAcquisitionHost( + IOptionsMonitor optionsMonitor, + IOptionsMonitor mergedOptionsMonitor, + IOptionsMonitor ccaOptionsMonitor) + { + _optionsMonitor = optionsMonitor; + _mergedOptionsMonitor = mergedOptionsMonitor; + _ccaOptionsMonitor = ccaOptionsMonitor; + } + + Task ITokenAcquisitionHost.GetAuthenticatedUserAsync(ClaimsPrincipal? user) + { + return null; + } + + string? ITokenAcquisitionHost.GetCurrentRedirectUri(MergedOptions mergedOptions) + { + return null; + } + + string ITokenAcquisitionHost.GetEffectiveAuthenticationScheme(string? authenticationScheme) + { + return authenticationScheme!; + } + + MergedOptions ITokenAcquisitionHost.GetOptions(string authenticationScheme) + { + var mergedOptions = _mergedOptionsMonitor.Get(authenticationScheme); + MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions( + /*authenticationScheme == null ? _optionsMonitor.CurrentValue :*/ _optionsMonitor.Get(authenticationScheme), + mergedOptions); + + DefaultCertificateLoader.UserAssignedManagedIdentityClientId = mergedOptions.UserAssignedManagedIdentityClientId; + return mergedOptions; + } + + SecurityToken? ITokenAcquisitionHost.GetTokenUsedToCallWebAPI() + { + return null; + } + + ClaimsPrincipal? ITokenAcquisitionHost.GetUserFromRequest() + { + return null; + } + + void ITokenAcquisitionHost.ProcessOptionsForAnonymousControllers(string authenticationScheme, MergedOptions mergedOptions) + { + } + + void ITokenAcquisitionHost.SetHttpResponse(HttpStatusCode statusCode, string wwwAuthenticate) + { + } + + void ITokenAcquisitionHost.SetSession(string key, string value) + { + } + } +} diff --git a/src/Microsoft.Identity.Web/ITokenAcquirer.cs b/src/Microsoft.Identity.Web/ITokenAcquirer.cs index c6d03a615..9520e3af4 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquirer.cs +++ b/src/Microsoft.Identity.Web/ITokenAcquirer.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; @@ -21,18 +20,18 @@ public interface ITokenAcquirer /// member of the controller's parameter. /// /// Scopes to request for the downstream API to call. + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. /// Optional claims principal representing the user. If not provided, will use the signed-in /// user (in a web app), or the user for which the token was received (in a web API) /// cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// An to call on behalf of the user, the downstream API characterized by its scopes. Task GetAuthenticationResultForUserAsync( IEnumerable scopes, + TokenAcquisitionOptions? tokenAcquisitionOptions = null, string? authenticationScheme = null, - ClaimsPrincipal? user = null, - TokenAcquisitionOptions? tokenAcquisitionOptions = null); + ClaimsPrincipal? user = null); /// /// Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -43,13 +42,13 @@ Task GetAuthenticationResultForUserAsync( /// Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration /// in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use /// several calls to get tokens for other resources). + /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - /// Options passed-in to create the token acquisition object which calls into MSAL .NET. /// An authentication result for the app itself, based on its scopes. Task GetAuthenticationResultForAppAsync( string scope, - string? authenticationScheme = null, - TokenAcquisitionOptions? tokenAcquisitionOptions = null); + TokenAcquisitionOptions? tokenAcquisitionOptions = null, + string? authenticationScheme = null); } } diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj index 7afb1d134..b1333ae34 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj @@ -131,6 +131,7 @@ + diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 77daa0234..cfe4d9a67 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -4,6 +4,326 @@ Microsoft.Identity.Web + + + Extension methods for . + + + + + Creates the from the values found + in an . + + The instance. + A built from . + + + + Extension methods related to App Services authentication (Easy Auth). + + + + + Add authentication with App Services. + + Authentication builder. + The builder, to chain commands. + + + + Default values related to AppServiceAuthentication handler. + + + + + The default value used for AppServiceAuthenticationOptions.AuthenticationScheme. + + + + + App service authentication handler. + + + + + Constructor for the AppServiceAuthenticationHandler. + Note the parameters are required by the base class. + + App service authentication options. + Logger factory. + URL encoder. + System clock. + + + + + + + Information about the App Services configuration on the host. + + + + + Is App Services authentication enabled?. + + + + + Logout URL for App Services Auth web sites. + + + + + ClientID of the App Services Auth web site. + + + + + Client secret of the App Services Auth web site. + + + + + Issuer of the App Services Auth web site. + + + + + Get headers from environment to help debugging App Services authentication. + + + + + Get the ID token from the headers sent by App services authentication. + + Headers. + The ID Token. + + + + Get the IDP from the headers sent by App services authentication. + + Headers. + The IDP. + + + + Get the user claims from the headers and environment variables. + + Headers. + User claims. + + + + Options for Azure App Services authentication. + + + + + Implementation of ITokenAcquisition for App Services authentication (EasyAuth). + + + + + Please call GetOrCreateApplication instead of accessing this field directly. + + + + + Constructor of the AppServicesAuthenticationTokenAcquisition. + + The App token cache provider. + Access to the HttpContext of the request. + HTTP client factory. + + + + + + + + + + + + + + + + + + + + + + + + + Filter used on a controller action to trigger incremental consent. + + + The following controller action will trigger. + + [AuthorizeForScopes(Scopes = new[] {"Mail.Send"})] + public async Task<IActionResult> SendEmail() + { + } + + + + + + Scopes to request. + + + + + Key section on the configuration file that holds the scope value. + + + + + Azure AD B2C user flow. + + + + + Allows specifying an AuthenticationScheme if OpenIdConnect is not the default challenge scheme. + + + + + Handles the . + + Context provided by ASP.NET Core. + + + + Finds an MsalUiRequiredException in one of the inner exceptions. + + Exception from which we look for an MsalUiRequiredException. + The MsalUiRequiredException if there is one, null, otherwise. + + + + Extensions for . + + + + + Enables an Azure Function to act as/expose a protected web API, enabling bearer token authentication. Calling this method from your Azure function validates the token and exposes the identity of the user or app on behalf of which your function is called, in the HttpContext.User member, where your function can make use of it. + + The current HTTP Context, such as req.HttpContext. + A task indicating success or failure. In case of failure . + + + + Azure SDK token credential based on the ITokenAcquisition service. + + + + + Constructor from an ITokenAcquisition service. + + Token acquisition. + + + + + + + + + + Factory class to create objects. + + + + + Instantiate a from an account object ID and tenant ID. This can + be useful when the web app subscribes to another service on behalf of the user + and then is called back by a notification where the user is identified by their tenant + ID and object ID (like in Microsoft Graph Web Hooks). + + Tenant ID of the account. + Object ID of the account in this tenant ID. + A containing these two claims. + + + + private async Task GetChangedMessagesAsync(IEnumerable<Notification> notifications) + { + HttpContext.User = ClaimsPrincipalExtension.FromTenantIdAndObjectId(subscription.TenantId, + subscription.UserId); + foreach (var notification in notifications) + { + SubscriptionStore subscription = + subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); + string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); + ...} + } + + + + + + Client assertion. + + + + + Constructor of a ClientAssertion, which can be used instead + of a client secret or client certificates to authenticate the + confidential client application. See https://aka.ms/ms-id-web/client-assertions. + + Signed assertion. + Optional expiry. + + + + Signed assertion. + + + + + Expiry of the client assertion. + + + + + Description of a client assertion in the application configuration. + See https://aka.ms/ms-id-web/client-assertions. + + + + + Constructor of a ClientAssertionDescription. + + delegate providing the client assertion + when it is necessary. + + + + delegate to get the client assertion. + + + + + Client assertion. + + + + + Get the signed assertion (and refreshes it if needed). + + Cancellation token. + The signed assertion. + + + + Expiry of the client assertion. + + Constants for claim types. @@ -169,342 +489,776 @@ Constants related to the log messages. - + - Encoding table. + Extension class containing cookie policies (work around for same site). - + - The following functions perform base64url encoding which differs from regular base64 encoding as follows - * padding is skipped so the pad character '=' doesn't have to be percent encoded - * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') - The changes make the encoding alphabet file and URL safe. + Handles SameSite cookie issue according to the https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1. + The default list of user agents that disallow "SameSite=None", + was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. - string to encode. - Base64Url encoding of the UTF8 bytes. + to update. + to chain. - + - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. + Handles SameSite cookie issue according to the docs: https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1 + The default list of user agents that disallow "SameSite=None", was taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. - An array of 8-bit unsigned integers. - The number of elements of inArray to convert. - An offset in inArray. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. + to update. + If you don't want to use the default user agent list implementation, + the method sent in this parameter will be run against the user agent and if returned true, SameSite value will be set to Unspecified. + The default user agent list used can be found at: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/. + to chain. - + - Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify - the subset as an offset in the input array, and the number of elements in the array to convert. + Checks if the specified user agent supports "SameSite=None" cookies. - An array of 8-bit unsigned integers. - The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - 'inArray' is null. - offset or length is negative OR offset plus length is greater than the length of inArray. + Browser user agent. + + Incompatible user agents include: + + Versions of Chrome from Chrome 51 to Chrome 66 (inclusive on both ends). + Versions of UC Browser on Android prior to version 12.13.2. + Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. + + Reference: https://www.chromium.org/updates/same-site/incompatible-clients. + + True, if the user agent does not allow "SameSite=None" cookie; otherwise, false. - + - Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. - base64Url encoded string. - UTF8 bytes. + Implementation for the downstream web API. + - + - Decodes the string from Base64UrlEncoded to UTF8. + Constructor. - string to decode. - UTF8 string. + Token acquisition service. + Named options provider. + HTTP client. + Configuration options. - + + + + + + + + + + - EventIds for Logging. + Merge the options from configuration and override from caller. + Named configuration. + Delegate to override the configuration. - + - Options passed-in to create the token acquisition object which calls into MSAL .NET. + Extension methods to support downstream web API services. - + - Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps - in the cases where a given account is a guest in other tenants, and you want to acquire tokens - for a specific tenant. + Adds a named downstream web API service related to a specific configuration section. - Can be the tenant ID or domain name. + Builder. + Name of the configuration for the service. + This is the name used when calling the service from controller/pages. + Configuration. + The builder for chaining. - + - Uses a particular user flow (in the case of AzureAD B2C) + Adds a named downstream web API service initialized with delegates. + Builder. + Name of the configuration for the service. + This is the name which will be used when calling the service from controller/pages. + Action to configure the options. + The builder for chaining. - + - Sets the correlation id to be used in the authentication request - to the /token endpoint. + Extensions for the downstream web API. - + - Sets Extra Query Parameters for the query string in the HTTP authentication request. + Get a strongly typed response from the web API. + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API with an HttpPost, providing strongly typed input and getting + strongly typed output. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Calls the web API endpoint with an HttpPut, providing strongly typed input data. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Calls the web API endpoint with an HttpPut, provinding strongly typed input data + and getting back strongly typed data. + + Output type. + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Path to the API endpoint relative to the base URL specified in the configuration. + Input data sent to the API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A strongly typed response from the web API. + + + + Call a web API endpoint with an HttpGet, + and return strongly typed data. + + Output type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. + + + + Call a web API with a strongly typed input, with an HttpGet. + + Input type. + The downstream web API. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input data. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The value returned by the downstream web API. - + - A string with one or multiple claims to request. - Normally used with Conditional Access. + Options passed-in to call downstream web APIs. To call Microsoft Graph, see rather + MicrosoftGraphOptions in the Microsoft.Identity.Web.MicrosoftGraph assembly. - + - Specifies if the token request will ignore the access token in the token cache - and will attempt to acquire a new access token. - If true, the request will ignore the token cache. The default is false. - Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, - for it induces performance degradation, as the token cache is not utilized. + Base URL for the called downstream web API. For instance "https://graph.microsoft.com/beta/".. - + - Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), - rather than a Bearer token. - PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, - which MSAL can manage. See https://aka.ms/msal-net-pop. + Path relative to the (for instance "me"). - + - Cancellation token to be used when calling the token acquisition methods. + HTTP method used to call this downstream web API (by default Get). - + - Key used for long running web APIs that need to call downstream web - APIs on behalf of the user. Can be null, if you are not developing a long - running web API, if you want - Microsoft.Identity.Web to allocate a session key for you, or your own string - if you want to associate the session with some information you have externally - (for instance a Microsoft Graph hook identifier). + Provides an opportunity to customize the HttpRequestMessage. For example, + to customize the headers. This is called after the message was formed, including + the Authorization header, and just before the message is sent. - + - Value that can be used for so that - MSAL.NET allocates the long running web api session key for the developer. + Clone the options (to be able to override them). + A clone of the options. - + + + Return the downstream web API URL. + + URL of the downstream web API. + + Clone the options (to be able to override them). A clone of the options. - + - OpenIdConnect options + Interface used to call a downstream web API, for instance from controllers. - + - Gets or sets the Authority to use when making OpenIdConnect calls. + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. - + - Client secret used to authenticate a confidential client app to AAD - (alternatively use client certificates) + Calls the downstream web API for the user, based on a description of the + downstream web API in the configuration. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful on platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + HTTP context in the case where is + , , . + An that the application will process. + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls a downstream web API consuming JSON with some data and returns data. + + Input type. + Output type. + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Input parameter to the downstream web API. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + [Optional] Claims representing a user. This is useful in platforms like Blazor + or Azure Signal R, where the HttpContext is not available. In other platforms, the library + will find the user from the HttpContext. + The value returned by the downstream web API. + + A list method that returns an IEnumerable<MyItem>>. + + public Task<IEnumerable<MyItem>> GetAsync() + { + return _downstreamWebApi.CallWebApiForUserAsync<object, IEnumerable<MyItem>>( + ServiceName, + null, + options => + { + options.RelativePath = $"api/todolist"; + }); + } + + + Example of editing. + + public Task<MyItem> EditAsync(MyItem myItem) + { + return _downstreamWebApi.CallWebApiForUserAsync<MyItem, MyItem>( + ServiceName, + nyItem, + options => + { + options.HttpMethod = HttpMethod.Patch; + options.RelativePath = $"api/todolist/{myItem.Id}"; + }); + } + + + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. + + + + Calls the downstream web API for the app, with the required scopes. + + Name of the service describing the downstream web API. There can + be several configuration named sections mapped to a , + each for one downstream web API. You can pass-in null, but in that case + needs to be set. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Overrides the options proposed in the configuration described + by . + HTTP content in the case where is + , , . + An that the application will process. - + - Gets or sets the 'client_id' (application ID) as appears in the - application registration. + A DelegatingHandler implementation that add an authorization header with a token for the application. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Initializes a new instance of the class. + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. - + + + + - Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". + Base class for Microsoft Identity authentication message handlers. - + - Gets or sets the tenant ID. + Gets the token acquisition service. - + - Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. + Initializes a new instance of the class. + Token acquisition service. + Named options provider. + Name of the service describing the downstream web API. - + - Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. + Gets the options for the specified request. + The request. + The configured options. - + - Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. + Base options passed-in to authenticate with Microsoft Identity. - + - Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. + Space separated scopes required to call the downstream web API. + For instance "user.read mail.read". - + - Gets the default user flow (which is signUpsignIn). + [Optional] tenant ID. This is used for specific scenarios where + the application needs to call a downstream web API on behalf of a user in several tenants. + It would mostly be used from code, not from the configuration. - + - Enables legacy ADAL cache serialization and deserialization. - Performance improvements when working with MSAL only apps. - Set to true if you have a shared cache with ADAL apps. + [Optional]. User flow (in the case of a B2C downstream web API). If not + specified, the B2C downstream web API will be called with the default user flow from + . - The default is false. - + - Is considered B2C if the attribute SignUpSignInPolicyId is defined. + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. + Set to true to enable PoP tokens automatically. - + - Is considered to have client credentials if the attribute ClientCertificates - or ClientSecret is defined. + Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Description of the certificates used to prove the identity of the web app or web API. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - An example in the appsetting.json: - - "ClientCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - - + - Description of the certificates used to decrypt an encrypted token in a web API. + Returns the scopes. - An example in the appsetting.json: - - "TokenDecryptionCertificates": [ - { - "SourceType": "StoreWithDistinguishedName", - "CertificateStorePath": "CurrentUser/My", - "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" - } - ] - - See also https://aka.ms/ms-id-web-certificates. - + Scopes. - + - Specifies if the x5c claim (public key of the certificate) should be sent to the STS. - Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: - this method will send the public certificate to Azure AD along with the token request, - so that Azure AD can use it to validate the subject name based on a trusted issuer policy. - This saves the application admin from the need to explicitly manage the certificate rollover - (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. + Extension for IHttpClientBuilder for startup initialization of Microsoft Identity authentication handlers. - The default is false. - + - Requests an auth code for the frontend (SPA using MSAL.js for instance). - See https://aka.ms/msal-net/spa-auth-code for details. + Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. - The default is false. + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. - + - Daemon applications can validate a token based on roles, or using the ACL-based authorization - pattern to control tokens without a roles claim. If using ACL-based authorization, - Microsoft Identity Web will not throw if roles or scopes are not in the Claims. - For details see https://aka.ms/ms-identity-web/daemon-ACL. + Adds a named Microsoft Identity user authentication message handler initialized with delegates. - The default is false. + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. - + - Used, when deployed to Azure, to specify explicitly a user assigned managed identity. - See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. + Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. + Builder. + Name of the configuration for the service. + Configuration. + The builder for chaining. - + - Sets the ResetPassword route path. - Defaults to /MicrosoftIdentity/Account/ResetPassword, - which is the value used by Microsoft.Identity.Web.UI. + Adds a named Microsoft Identity application authentication message handler initialized with delegates. + Builder. + Name of the configuration for the service. + Action to configure the options. + The builder for chaining. - + - Sets the Error route path. - Defaults to the value /MicrosoftIdentity/Account/Error, - which is the value used by Microsoft.Identity.Web.UI. + Options passed-in to Microsoft Identity message handlers. - + - Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. + Clone the options (to be able to override them). + A clone of the options. - + - Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). + Clone the options (to be able to override them). + + A clone of the options. + + + + A DelegatingHandler implementation that add an authorization header with a token on behalf of the current user. - + - In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user - signed-in and consented) - the token redeemed from the "/> is added to the cache, so that it can then be used to acquire another token on-behalf-of the - same user in order to call to downstream APIs. + Initializes a new instance of the class. - Scopes to request. Can be empty - Authorization code - Authentication scheme to use (config section) - Client Info obtained with the code - PKCE code verifier - User flow in the case of B2C - The ID Token. + Token acquisition service. + Named options provider. + Configuration options. + Name of the service describing the downstream web API. - + + + + - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Extension methods. - Signed in user + + + Determines whether the specified string collection contains any. + The search for. + The string collection. + + true if the specified string collection contains any; otherwise, false. + + + + Keep the validated token associated with the HTTP request. + + HTTP context. + Token to preserve after the token is validated so that + it can be used in the actions. + + + + Get the parsed information about the token used to call the web API. + + HTTP context associated with the current request. + used to call the web API. + + + + Provides access to get or set the current error status. + The default implementation will use TempData and be enabled when run under Development. + + + + + Gets the error message for the current request. + + Current . + The current error message if available. + + + + Sets the error message for the current request. + + Current . + Error message to set. + + + + Gets whether error messages should be displayed. + + + + + Helper methods to handle incremental consent and conditional access in + a web app. + + + + + Can the exception be solved by re-signing-in the user?. + + Exception from which the decision will be made. + Returns true if the issue can be solved by signing-in + the user, and false, otherwise. + + + + Build authentication properties needed for incremental consent. + + Scopes to request. + instance. + User. + Userflow being invoked for AAD B2C. + AuthenticationProperties. + + + + An implementation of IConfigurationRetriever geared towards Azure AD issuers metadata. + + + + Retrieves a populated configuration given an address and an . + Address of the discovery document. + The to use to read the discovery document. + A cancellation token that can be used by other objects or threads to receive notice of cancellation. . + + A that, when completed, returns from the configuration. + + address - Azure AD Issuer metadata address URL is required + or retriever - No metadata document retriever is provided. + + + + Model class to hold information parsed from the Azure AD issuer endpoint. + + + + + Issuer associated with the OIDC endpoint. + + + + + Interface for the token acquisition service (encapsulating MSAL.NET). + + + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Options passed-in to create the token acquisition object which calls into MSAL .NET. Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - A that represents a completed remove from cache operation. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + An to call on behalf of the user, the downstream API characterized by its scopes. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Options passed-in to create the token acquisition object which calls into MSAL .NET. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + An authentication result for the app itself, based on its scopes. Interface for the token acquisition service (encapsulating MSAL.NET). + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the + member of the controller's parameter. + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call on behalf of the user, the downstream API characterized by its scopes. + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -522,6 +1276,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token to call on behalf of the user, the downstream API characterized by its scopes. + + + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token + for a downstream API on behalf of the user account for which the claims are provided in the current user + + Scopes to request for the downstream API to call. + Enables to override the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An to call on behalf of the user, the downstream API characterized by its scopes. + Typically used from an ASP.NET Core web app or web API controller. This method gets an access token @@ -539,6 +1308,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An to call on behalf of the user, the downstream API characterized by its scopes. + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -556,6 +1340,21 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An access token for the app itself, based on its scopes. + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) @@ -573,291 +1372,1672 @@ Options passed-in to create the token acquisition object which calls into MSAL .NET. An authentication result for the app itself, based on its scopes. - + - Get the effective authentication scheme based on the context. + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. - Proposed authentication scheme. - Effective authenticationScheme which is the authentication scheme - if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme + Scopes to consent to. + triggering the challenge. + The to update. + + + + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + + Scopes to consent to. + triggering the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + Get the effective authentication scheme based on the context. + + Proposed authentication scheme. + Effective authenticationScheme which is the authentication scheme + if it's not null, or otherwise OpenIdConnectDefault.AuthenticationScheme if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - + - Token acquisition service. + Used in web APIs (which therefore cannot have an interaction with the user). + Replies to the client through the HttpResponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that + the client can trigger an interaction with the user so the user can consent to more scopes. + Scopes to consent to. + triggering the challenge. + The to update. + A representing the asynchronous operation. + + - LoggingMessage class for TokenAcquisition. + Interface for the internal operations of token acquisition service (encapsulating MSAL.NET). - + + + In a web app, adds, to the MSAL.NET cache, the account of the user authenticating to the web app, when the authorization code is received (after the user + signed-in and consented) + An On-behalf-of token contained in the is added to the cache, so that it can then be used to acquire another token on-behalf-of the + same user in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + Scopes to request. + Authentication scheme to use. + A that represents a completed add to cache operation. + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + + + - Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Signed in user + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + A that represents a completed remove from cache operation. - + - Scopes which are already requested by MSAL.NET. They should not be re-requested;. + EventIds for Logging. - + - Meta-tenant identifiers which are not allowed in client credentials. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. + Merges the MicrosoftIdentityWebOptions and the ConfidentialClientApplicationOptions. - + - Constructor of the TokenAcquisition service. This requires the Azure AD Options to - configure the confidential client application and a token cache provider. - This constructor is called by ASP.NET Core dependency injection. + Base class for web app and web API Microsoft Identity authentication + builders. - The App token cache provider. - Host of the token acquisition. - HTTP client factory. - Logger. - Service provider. - + - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from claims provided in the current claims principal. - instance of the current HttpContext. + Constructor. - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. - Options passed-in to create the token acquisition options object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + The services being configured. + Optional configuration section. - + - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + The services being configured. - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An authentication result for the app itself, based on its scopes. - + - Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + Configuration section from which to bind options. - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - AuthenticationScheme to use. - Enables overriding of the tenant/account for the same identity. This is useful - for multi tenant apps or daemons. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token for the app itself, based on its scopes. + It can be null if the configuration happens with delegates + rather than configuration. - + - Typically used from a web app or web API controller, this method retrieves an access token - for a downstream API using; - 1) the token cache (for web apps and web APIs) if a token exists in the cache - 2) or the on-behalf-of flow - in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. - instance of the current HttpContext. + Extensions for IServerSideBlazorBuilder for startup initialization of web APIs. - Scopes to request for the downstream API to call. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - Enables overriding of the tenant/account for the same identity. This is useful in the - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Azure AD B2C user flow to target. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a web API supposes that you have previously called, - in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method - passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that - you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by - OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - + - Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + Add the incremental consent and conditional access handler for Blazor + server side pages. - User - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - A that represents a completed account removal operation. + Service side blazor builder. + The builder. - + - Creates an MSAL confidential client application. + Add the incremental consent and conditional access handler for + web app pages, Razor pages, controllers, views, etc... + Service collection. + The service collection. - + - Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + Handler for Blazor specific APIs to handle incremental consent + and conditional access. - . - Claims principal for the user on behalf of whom to get a token. - Scopes for the downstream API to call. - (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user described in the claimsPrincipal. - Merged options. - Azure AD B2C user flow to target. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - + - Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. + Initializes a new instance of the class. - . - User IAccount for which to acquire a token. - See . - Scopes for the downstream API to call. - TenantID based on a specific tenant for which to acquire a token to access the scopes - on behalf of the user. - Merged options. - Azure AD B2C user flow. - Options passed-in to create the token acquisition object which calls into MSAL .NET. + Service provider to get the HttpContextAccessor for the current HttpContext, when available. - + - Logger for handling MSAL exceptions in TokenAcquisition. + Boolean to determine if server is Blazor. - ILogger. - Specific log message from TokenAcquisition. - Exception from MSAL.NET. - + - Logger for handling information specific to MSAL in token acquisition. + Current user. - ILogger. - durationTotalInMs. - durationInHttpInMs. - durationInCacheInMs. - cache or IDP. - correlationId. - cacheRefreshReason. - Exception from MSAL.NET. - + - Prototype of certificate-less authentication using a signed assertion - acquired with MSI (federated identity). + Base URI to use in forming the redirect. - The signed assertion. - + - Client assertion. + For Blazor/Razor pages to process the exception from + a user challenge. + Exception. - + - Constructor of a ClientAssertion, which can be used instead - of a client secret or client certificates to authenticate the - confidential client application. See https://aka.ms/ms-id-web/client-assertions. + Forces the user to consent to specific scopes and perform + Conditional Access to get specific claims. Use on a Razor/Blazor + page or controller to proactively ensure the scopes and/or claims + before acquiring a token. The other mechanism + ensures claims and scopes requested by Azure AD after a failed token acquisition attempt. + See https://aka.ms/ms-id-web/ca_incremental-consent for details. - Signed assertion. - Optional expiry. + Scopes to request. + Claims to ensure. + Userflow being invoked for AAD B2C. - + - Signed assertion. + Options for configuring authentication using Azure Active Directory. It has both AAD and B2C configuration attributes. - + - Expiry of the client assertion. + Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". - + - Description of a client assertion in the application configuration. - See https://aka.ms/ms-id-web/client-assertions. + Gets or sets the tenant ID. - + - Constructor of a ClientAssertionDescription. + Gets or sets the domain of the Azure Active Directory tenant, e.g. contoso.onmicrosoft.com. - delegate providing the client assertion - when it is necessary. - + - delegate to get the client assertion. + Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. - + - Client assertion. + Gets or sets the sign up or sign in user flow name for B2C, e.g. b2c_1_susi. - + - Get the signed assertion (and refreshes it if needed). + Gets or sets the reset password user flow name for B2C, e.g. B2C_1_password_reset. - Cancellation token. - The signed assertion. - + - Expiry of the client assertion. + Gets the default user flow (which is signUpsignIn). - + - Microsoft Identity Web specific exception class for - use in Blazor or Razor pages to process the user challenge. - Handles the . + Enables legacy ADAL cache serialization and deserialization. + Performance improvements when working with MSAL only apps. + Set to true if you have a shared cache with ADAL apps. + The default is false. - + - Exception thrown by MSAL when a user challenge is encountered. + Is considered B2C if the attribute SignUpSignInPolicyId is defined. - + - Scopes to request. + Is considered to have client credentials if the attribute ClientCertificates + or ClientSecret is defined. - + - Specified userflow. + Description of the certificates used to prove the identity of the web app or web API. + An example in the appsetting.json: + + "ClientCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + - + - Handles the user challenge for Blazor or Razor pages. + Description of the certificates used to decrypt an encrypted token in a web API. - Exception thrown by MSAL when a user challenge is encountered. - Scopes to request. - Userflow used in B2C. + An example in the appsetting.json: + + "TokenDecryptionCertificates": [ + { + "SourceType": "StoreWithDistinguishedName", + "CertificateStorePath": "CurrentUser/My", + "CertificateDistinguishedName": "CN=WebAppCallingWebApiCert" + } + ] + + See also https://aka.ms/ms-id-web-certificates. + + + + + Specifies if the x5c claim (public key of the certificate) should be sent to the STS. + Sending the x5c enables application developers to achieve easy certificate rollover in Azure AD: + this method will send the public certificate to Azure AD along with the token request, + so that Azure AD can use it to validate the subject name based on a trusted issuer policy. + This saves the application admin from the need to explicitly manage the certificate rollover + (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni. + + The default is false. + + + + Requests an auth code for the frontend (SPA using MSAL.js for instance). + See https://aka.ms/msal-net/spa-auth-code for details. + + The default is false. + + + + Daemon applications can validate a token based on roles, or using the ACL-based authorization + pattern to control tokens without a roles claim. If using ACL-based authorization, + Microsoft Identity Web will not throw if roles or scopes are not in the Claims. + For details see https://aka.ms/ms-identity-web/daemon-ACL. + + The default is false. + + + + Used, when deployed to Azure, to specify explicitly a user assigned managed identity. + See https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal. + + + + + Sets the ResetPassword route path. + Defaults to /MicrosoftIdentity/Account/ResetPassword, + which is the value used by Microsoft.Identity.Web.UI. + + + + + Sets the Error route path. + Defaults to the value /MicrosoftIdentity/Account/Error, + which is the value used by Microsoft.Identity.Web.UI. + + + + + Microsoft Identity Web specific exception class for + use in Blazor or Razor pages to process the user challenge. + Handles the . + + + + + Exception thrown by MSAL when a user challenge is encountered. + + + + + Scopes to request. + + + + + Specified userflow. + + + + + Handles the user challenge for Blazor or Razor pages. + + Exception thrown by MSAL when a user challenge is encountered. + Scopes to request. + Userflow used in B2C. + + + + Prototype of certificate-less authentication using a signed assertion + acquired with MSI (federated identity). + + The signed assertion. + + + + This is the metadata that describes required auth scopes for a given endpoint + in a web API. It's the underlying data structure the requirement will look for + in order to validate scopes in the scope claims. + + + + + Scopes accepted by this web API. + + + + + Fully qualified name of the configuration key containing the required scopes (separated + by spaces). + + + + + Extensions for building the RequiredScope policy during application startup. + + + + services.AddAuthorization(o => + { o.AddPolicy("Custom", + policyBuilder =>policyBuilder.RequireScope("access_as_user")); + }); + + + + + + Adds a to the current instance which requires + that the current user has the specified claim and that the claim value must be one of the allowed values. + + Used for building policies during application startup. + Values the claim must process one or more of for evaluation to succeed. + A reference to this instance after the operation has completed. + + + + Adds a to the current instance which requires + that the current user has the specified claim and that the claim value must be one of the allowed values. + + Used for building policies during application startup. + Values the claim must process one or more of for evaluation to succeed. + A reference to this instance after the operation has completed. + + + + This attribute is used on a controller, pages, or controller actions + to declare (and validate) the scopes required by a web API. These scopes can be declared + in two ways: hardcoding them, or declaring them in the configuration. Depending on your + choice, use either one or the other of the constructors. + For details, see https://aka.ms/ms-id-web/required-scope-attribute. + + + + + Scopes accepted by this web API. + + + + + Fully qualified name of the configuration key containing the required scopes (separated + by spaces). + + + If the appsettings.json file contains a section named "AzureAd", in which + a property named "Scopes" contains the required scopes, the attribute on the + controller/page/action to protect should be set to the following: + + [RequiredScope(RequiredScopesConfigurationKey="AzureAd:Scopes")] + + + + + + Verifies that the web API is called with the right scopes. + If the token obtained for this API is on behalf of the authenticated user does not have + any of these in its scope claim, the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + + Scopes accepted by this web API. + When the scopes don't match, the response is a 403 (Forbidden), + because the user is authenticated (hence not 401), but not authorized. + + Add the following attribute on the controller/page/action to protect: + + + [RequiredScope("access_as_user")] + + + and + if you want to express the required scopes from the configuration. + + + + Default constructor. + + + + [RequiredScope(RequiredScopesConfigurationKey="AzureAD:Scope")] + class Controller : BaseController + { + } + + + + + + Unused: Compatibility of interface with the Authorization Filter. + + + + + Interface implemented by diagnostics for the JWT Bearer middleware. + + + + + Called to subscribe to . + + JWT Bearer events. + The events (for chaining). + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Method to subscribe to . + + OpenID Connect events. + + + + Diagnostics for the JwtBearer middleware (used in web APIs). + + + + + Constructor for a . This constructor + is used by dependency injection. + + Logger. + + + + Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after the security token has passed validation and a ClaimsIdentity has been generated. + + + + + Invoked before a challenge is sent back to the caller. + + + + + Subscribes to all the JwtBearer events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + for chaining. + + + + Factory class for creating the IssuerValidator per authority. + + + + + Initializes a new instance of the class. + + Options passed-in to create the AadIssuerValidator object. + HttpClientFactory. + + + + Gets an for an authority. + + The authority to create the validator for, e.g. https://login.microsoftonline.com/. + A for the aadAuthority. + if is null or empty. + + + + Diagnostics used in the OpenID Connect middleware + (used in web apps). + + + + + Constructor of the , used + by dependency injection. + + Logger used to log the diagnostics. + + + + Invoked before redirecting to the identity provider to authenticate. This can + be used to set ProtocolMessage.State that will be persisted through the authentication + process. The ProtocolMessage can also be used to add or customize parameters + sent to the identity provider. + + + + + Invoked when a protocol message is first received. + + + + + Invoked after security token validation if an authorization code is present + in the protocol message. + + + + + Invoked after "authorization code" is redeemed for tokens at the token endpoint. + + + + + Invoked when an IdToken has been validated and produced an AuthenticationTicket. + + + + + Invoked when user information is retrieved from the UserInfoEndpoint. + + + + + Invoked if exceptions are thrown during request processing. The exceptions will + be re-thrown after this event unless suppressed. + + + + + Invoked when a request is received on the RemoteSignOutPath. + + + + + Invoked before redirecting to the identity provider to sign out. + + + + + Invoked before redirecting to the Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutRedirectUri + at the end of a remote sign-out flow. + + + + + Subscribes to all the OpenIdConnect events, to help debugging, while + preserving the previous handlers (which are called). + + Events to subscribe to. + + + + Generic class that registers the token audience from the provided Azure AD authority. + + + + + Default validation of the audience: + - when registering an Azure AD web API in the app registration portal (and adding a scope) + the default App ID URI generated by the portal is api://{clientID} + - However, the audience (aud) of the token acquired to access this web API is different depending + on the "accepted access token version" for the web API: + - if accepted token version is 1.0, the audience provided in the token + by the Microsoft identity platform (formerly Azure AD v2.0) endpoint is: api://{ClientID} + - if the accepted token version is 2.0, the audience provided by Azure AD v2.0 in the token + is {CliendID} + When getting an access token for an Azure AD B2C web API the audience in the token is + api://{ClientID}. + + When web API developers don't provide the "Audience" in the configuration, Microsoft.Identity.Web + considers that this is the default App ID URI as explained above. When developer provides the + "Audience" member, it's available in the TokenValidationParameter.ValidAudience. + + Audiences in the security token. + Security token from which to validate the audiences. + Token validation parameters. + True if the token is valid; false, otherwise. + + + + Extension class providing the extension methods for that + can be used in web APIs to validate the roles in controller actions. + + + + + When applied to an , verifies that the application + has the expected roles. + + HttpContext (from the controller). + Roles accepted by this web API. + When the roles don't match, the response is a 403 (Forbidden), + because the app does not have the expected roles. + + + + Extension class providing the extension + methods for that + can be used in web APIs to validate scopes in controller actions. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. + + + + + When applied to an , verifies that the user authenticated in the + web API has any of the accepted scopes. + If there is no authenticated user, the response is a 401 (Unauthenticated). + If the authenticated user does not have any of these , the + method updates the HTTP response providing a status code 403 (Forbidden) + and writes to the response body a message telling which scopes are expected in the token. + We recommend using instead the RequiredScope Attribute on the controller, the page or the action. + See https://aka.ms/ms-id-web/required-scope-attribute. + + HttpContext (from the controller). + Scopes accepted by this web API. + + + + Extensions for building the required scope attribute during application startup. + + + + + This method adds support for the required scope attribute. It adds a default policy that + adds a scope requirement. This requirement looks for IAuthRequiredScopeMetadata on the current endpoint. + + The services being configured. + Services. + + + + This method adds metadata to route endpoint to describe required scopes. It's the imperative version of + the [RequiredScope] attribute. + + Class implementing . + To customize the endpoints. + Scope. + Builder. + + + + RequireScopeOptions. + + + + + Sets the default policy. + + + + + + + + Scope authorization handler that needs to be called for a specific requirement type. + In this case, . + + + + + Constructor for the scope authorization handler, which takes a configuration. + + Configuration. + + + + Makes a decision if authorization is allowed based on a specific requirement. + + AuthorizationHandlerContext. + Scope authorization requirement. + Task. + + + + Implements an + which requires at least one instance of the specified claim type, and, if allowed values are specified, + the claim value must be any of the allowed values. + + + + + Creates a new instance of . + + The optional list of scope values. + + + + Gets the optional list of scope values. + + + + + Gets the optional list of scope values from configuration. + + + + + + + + Options passed-in to create the AadIssuerValidator object. + + + + + Sets the name of the HttpClient to get from the IHttpClientFactory for use with the configuration manager. + Needed when customizing the client such as configuring a proxy. + + + + + Extensions for IServiceCollection for startup initialization of web APIs. + + + + + Add the token acquisition service. + + Service collection. + Specifies if an instance of should be a singleton. + The service collection. + + This method is typically called from the ConfigureServices(IServiceCollection services) in Startup.cs. + Note that the implementation of the token cache can be chosen separately. + + + // Token acquisition service and its cache implementation as a session cache + services.AddTokenAcquisition() + .AddDistributedMemoryCache() + .AddSession() + .AddSessionBasedTokenCache(); + + + + + + An implementation of that uses to track error messages. + + + + + Token acquisition service. + + + LoggingMessage class for TokenAcquisition. + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + The App token cache provider. + Host of the token acquisition service. + HTTP client factory. + Logger. + Service provider. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + The to update. + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + + + + Used in web APIs (no user interaction). + Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that + the client, in turn, can trigger a user interaction so that the user consents to more scopes. + + Scopes to consent to. + The that triggered the challenge. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + The to update. + + + + This handler is executed after the authorization code is received (once the user signs-in and consents) during the + authorization code flow in a web app. + It uses the code to request an access token from the Microsoft identity platform and caches the tokens and an entry about the signed-in user's account in the MSAL's token cache. + The access token (and refresh token) provided in the , once added to the cache, are then used to acquire more tokens using the + on-behalf-of flow for the signed-in user's account, + in order to call to downstream APIs. + + The context used when an 'AuthorizationCode' is received over the OpenIdConnect protocol. + scopes to request access to. + Authentication scheme to use (by default, OpenIdConnectDefaults.AuthenticationScheme). + + From the configuration of the Authentication of the ASP.NET Core web API: + OpenIdConnectOptions options; + + Subscribe to the authorization code received event: + + options.Events = new OpenIdConnectEvents(); + options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived; + } + + + And then in the OnAuthorizationCodeRecieved method, call : + + private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); + await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, new string[] { "user.read" }); + } + + + + + + Please call GetOrBuildConfidentialClientApplication instead of accessing this field directly. + + + + + Scopes which are already requested by MSAL.NET. They should not be re-requested;. + + + + + Meta-tenant identifiers which are not allowed in client credentials. + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + The App token cache provider. + Host of the token acquisition. + HTTP client factory. + Logger. + Service provider. + + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from claims provided in the current claims principal. + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest. + Options passed-in to create the token acquisition options object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An authentication result for the app itself, based on its scopes. + + + + Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) + using the client credentials flow. See https://aka.ms/msal-net-client-credentials. + + The scope requested to access a protected API. For this flow (client credentials), the scope + should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft + Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration + in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use + several calls to get tokens for other resources). + AuthenticationScheme to use. + Enables overriding of the tenant/account for the same identity. This is useful + for multi tenant apps or daemons. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token for the app itself, based on its scopes. + + + + Typically used from a web app or web API controller, this method retrieves an access token + for a downstream API using; + 1) the token cache (for web apps and web APIs) if a token exists in the cache + 2) or the on-behalf-of flow + in web APIs, for the user account that is ascertained from the claims provided in the current claims principal. + instance of the current HttpContext. + + Scopes to request for the downstream API to call. + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + Enables overriding of the tenant/account for the same identity. This is useful in the + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + An access token to call the downstream API and populated with this downstream API's scopes. + Calling this method from a web API supposes that you have previously called, + in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + passing the validated token (as a JwtSecurityToken). Calling it from a web app supposes that + you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. + + + + Removes the account associated with context.HttpContext.User from the MSAL.NET cache. + + User + Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme + if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. + A that represents a completed account removal operation. + + + + Creates an MSAL confidential client application. + + + + + Gets an access token for a downstream API on behalf of the user described by its claimsPrincipal. + + . + Claims principal for the user on behalf of whom to get a token. + Scopes for the downstream API to call. + (optional) TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user described in the claimsPrincipal. + Merged options. + Azure AD B2C user flow to target. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + Gets an access token for a downstream API on behalf of the user whose account is passed as an argument. + + . + User IAccount for which to acquire a token. + See . + Scopes for the downstream API to call. + TenantID based on a specific tenant for which to acquire a token to access the scopes + on behalf of the user. + Merged options. + Azure AD B2C user flow. + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + Logger for handling MSAL exceptions in TokenAcquisition. + + ILogger. + Specific log message from TokenAcquisition. + Exception from MSAL.NET. + + + + Logger for handling information specific to MSAL in token acquisition. + + ILogger. + durationTotalInMs. + durationInHttpInMs. + durationInCacheInMs. + cache or IDP. + correlationId. + cacheRefreshReason. + Exception from MSAL.NET. + + + + Implementation of ITokenAcquisitionHost in the case of ASP.NET Core + + + + + Constructor of the TokenAcquisition service. This requires the Azure AD Options to + configure the confidential client application and a token cache provider. + This constructor is called by ASP.NET Core dependency injection. + + Access to the HttpContext of the request. + Configuration options. + Logger. + Service provider. + + + + + + + Options passed-in to create the token acquisition object which calls into MSAL .NET. + + + + + Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps + in the cases where a given account is a guest in other tenants, and you want to acquire tokens + for a specific tenant. + + Can be the tenant ID or domain name. + + + + Uses a particular user flow (in the case of AzureAD B2C) + + + + + Sets the correlation id to be used in the authentication request + to the /token endpoint. + + + + + Sets Extra Query Parameters for the query string in the HTTP authentication request. + + + + + A string with one or multiple claims to request. + Normally used with Conditional Access. + + + + + Specifies if the token request will ignore the access token in the token cache + and will attempt to acquire a new access token. + If true, the request will ignore the token cache. The default is false. + Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, + for it induces performance degradation, as the token cache is not utilized. + + + + + Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), + rather than a Bearer token. + PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, + which MSAL can manage. See https://aka.ms/msal-net-pop. + + + + + Cancellation token to be used when calling the token acquisition methods. + + + + + Key used for long running web APIs that need to call downstream web + APIs on behalf of the user. Can be null, if you are not developing a long + running web API, if you want + Microsoft.Identity.Web to allocate a session key for you, or your own string + if you want to associate the session with some information you have externally + (for instance a Microsoft Graph hook identifier). + + + + + Value that can be used for so that + MSAL.NET allocates the long running web api session key for the developer. + + + + + Clone the options (to be able to override them). + + A clone of the options. + + + + An implementation of token cache for confidential clients backed by an HTTP session. + + + For this session cache to work effectively, the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + + https://aka.ms/msal-net-token-cache-serialization + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + MSAL Token cache provider constructor. + + Session for the current user. + Logger. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Read blob. + + + + Read a blob representing the token cache from its key. + + Key representing the token cache + (account or app). + Hints for the cache serialization implementation optimization. + Read blob. + + + + Writes the token cache identified by its key to the serialization mechanism. + + Key for the cache (account ID or app ID). + Blob to write to the cache. + A that completes when a write operation has completed. + + + + Removes a cache described by its key. + + Key of the token cache (user account or app ID). + A that completes when key removal has completed. + + + + LoggingMessage class for MsalSessionTokenCacheProvider. + + + + + Session cache logging. + + ILogger. + /// Cache operation (Read, Write, etc...). + Session Id. + MSAL.NET cache key. + Exception. + + + + Session cache deserialized. + + ILogger. + MSAL.NET cache key. + Session Id. + Exception. + + + + Extension class to add a session token cache serializer to MSAL. + + + + + Adds an HTTP session-based application token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + + + + Adds an HTTP session-based per-user token cache to the service collection. + + + For this session cache to work effectively the ASP.NET Core session has to be configured properly. + The latest guidance is provided at https://docs.microsoft.com/aspnet/core/fundamentals/app-state. + + In the method public void ConfigureServices(IServiceCollection services) in Startup.cs, add the following: + + services.AddSession(option => + { + option.Cookie.IsEssential = true; + }); + + In the method public void Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs, add the following: + + app.UseSession(); // Before UseMvc() + + Because session token caches are added with scoped lifetime, they should not be used when TokenAcquisition is also used as a singleton (for example, when using Microsoft Graph SDK). + + The services collection to add to. + The service collection. + + + + Encoding table. + + + + + The following functions perform base64url encoding which differs from regular base64 encoding as follows + * padding is skipped so the pad character '=' doesn't have to be percent encoded + * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_') + The changes make the encoding alphabet file and URL safe. + + string to encode. + Base64Url encoding of the UTF8 bytes. + + + + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. + + An array of 8-bit unsigned integers. + The number of elements of inArray to convert. + An offset in inArray. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. + + + + Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify + the subset as an offset in the input array, and the number of elements in the array to convert. + + An array of 8-bit unsigned integers. + The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + 'inArray' is null. + offset or length is negative OR offset plus length is greater than the length of inArray. + + + + Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. + base64Url encoded string. + UTF8 bytes. + + + + Decodes the string from Base64UrlEncoded to UTF8. + + string to decode. + UTF8 string. + + + + Authentication builder for a web API. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + ACtion called to configure the JwtBearer options. + Action called to configure + the Microsoft identity options. + Configuration section from which to + get parameters. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + + The action to configure . + The authentication builder to chain. + + + + Extensions for for startup initialization of web APIs. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration second from which to fill-in the options. + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + + The authentication builder to chain. + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + + The to which to add this configuration. + The action to configure . + The action to configure the . + The JWT bearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JWT bearer events. + The authentication builder to chain. + + + + Builder for web API authentication with configuration. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0). + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The authentication builder to chain. + + + + Extension for IServiceCollection for startup initialization of web APIs. + + + + + Protects the web API with Microsoft identity platform (formerly Azure AD v2.0) + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + Service collection to which to add authentication. + The Configuration object. + The configuration section with the necessary settings to initialize authentication options. + The JwtBearer scheme name to be used. By default it uses "Bearer". + + Set to true if you want to debug, or just understand the JwtBearer events. + The authentication builder to chain extension methods. + + + + Web API authentication builder. + + + + + Allows a higher level abstraction of security token (i.e. System.IdentityModel.Tokens.Jwt and more modern, Microsoft.IdentityModel.JsonWebTokens) + to be used with Microsoft Identity Web. + Developers should continue to use `EnableTokenAcquisitionToCallDownstreamApi`. + This API is not considered part of the public API and may change. + + The action to configure . + Authentication scheme. + The services being configured. + IConfigurationSection. + The authentication builder to chain. + + + + Authentication builder returned by the EnableTokenAcquisitionToCallDownstreamApi methods + enabling you to decide token cache implementations. + + + + + Add in memory token caches. + + to configure. + to configure. + the service collection. + + + + Add distributed token caches. + + the service collection. + + + + Add session token caches. + + the service collection. + + + + Authentication builder specific for Microsoft identity platform. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + The web app calls a web API. + + Initial scopes. + The builder itself for chaining. + + + + The web app calls a web API. This override enables you to specify the + ConfidentialClientApplicationOptions (from MSAL.NET) programmatically. + + Action to configure the + MSAL.NET confidential client application options. + Initial scopes. + The builder itself for chaining. + + + + Extensions for the for startup initialization. + + + + + Add authentication to a web app with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, + with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration instance. + The configuration section with the necessary settings to initialize authentication options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The builder for chaining. + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. + + The to which to add this configuration. + The configuration section from which to get the options. + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + Configuration section. + The authentication builder for chaining. + + + + Add authentication with Microsoft identity platform. + + The to which to add this configuration. + The action to configure . + The action to configure . + The OpenID Connect scheme name to be used. By default it uses "OpenIdConnect". + The cookie-based scheme name to be used. By default it uses "Cookies". + Set to true if you want to debug, or just understand the OpenID Connect events. + A display name for the authentication handler. + The authentication builder for chaining. + + + + Builder for a Microsoft identity web app authentication where configuration is + available for EnableTokenAcquisitionToCallDownstreamApi. + + + + + Constructor. + + The services being configured. + Default scheme used for OpenIdConnect. + Action called to configure + the Microsoft identity options. + Optional configuration section. + + + + Add support for the web app to acquire tokens to call an API. + + Optional initial scopes to request. + The authentication builder for chaining. + + + + Extension for IServiceCollection for startup initialization. + + + + + Add authentication with Microsoft identity platform. + This method expects the configuration file will have a section, (by default named "AzureAd"), with the necessary settings to + initialize the authentication options. + + Service collection to which to add authentication. + The IConfiguration object. + The name of the configuration section with the necessary + settings to initialize authentication options. + Optional name for the open id connect authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). This can be specified when you want to support + several OpenIdConnect identity providers. + Optional name for the cookie authentication scheme + (by default OpenIdConnectDefaults.AuthenticationScheme). + Set to true if you want to debug, or just understand the OpenIdConnect events. + A display name for the authentication handler. + The authentication builder to chain extension methods. diff --git a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs index 187e1b0e0..7c1ec6417 100644 --- a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs +++ b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs @@ -59,7 +59,7 @@ public class MicrosoftIdentityOptions : OpenIdConnectOptions /// /// Gets or sets the Azure Active Directory instance, e.g. "https://login.microsoftonline.com". /// - public string Instance { get; set; } = null!; + public string Instance { get; set; } = "https://login.microsoftonline.com/"; /// /// Gets or sets the tenant ID. diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index e39b20e75..fe3cbe13e 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Identity.Web.Hosts; namespace Microsoft.Identity.Web { @@ -41,6 +42,7 @@ public static IServiceCollection AddTokenAcquisition( ServiceDescriptor? tokenAcquisitionService = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisition)); ServiceDescriptor? tokenAcquisitionInternalService = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisitionInternal)); + ServiceDescriptor? tokenAcquisitionhost = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisitionHost)); if (tokenAcquisitionService != null && tokenAcquisitionInternalService != null) { if (isTokenAcquisitionSingleton ^ (tokenAcquisitionService.Lifetime == ServiceLifetime.Singleton)) @@ -48,6 +50,7 @@ public static IServiceCollection AddTokenAcquisition( // The service was already added, but not with the right lifetime services.Remove(tokenAcquisitionService); services.Remove(tokenAcquisitionInternalService); + services.Remove(tokenAcquisitionhost); } else { @@ -57,18 +60,27 @@ public static IServiceCollection AddTokenAcquisition( } // Token acquisition service - services.AddHttpContextAccessor(); if (isTokenAcquisitionSingleton) { services.AddSingleton(); +#if !NET472 && !NET462 + services.AddHttpContextAccessor(); services.AddSingleton(); services.AddSingleton(s => (ITokenAcquisitionInternal)s.GetRequiredService()); +#else + services.AddSingleton(); +#endif } else { services.AddScoped(); +#if !NET472 && !NET462 + services.AddHttpContextAccessor(); services.AddScoped(); services.AddScoped(s => (ITokenAcquisitionInternal)s.GetRequiredService()); +#else + services.AddScoped(); +#endif } return services; diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs index 82bff3837..8ec87e7f6 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs @@ -89,6 +89,8 @@ public TokenAcquisitionOptions Clone() { return new TokenAcquisitionOptions { + Tenant = Tenant, + UserFlow = UserFlow, CorrelationId = CorrelationId, ExtraQueryParameters = ExtraQueryParameters, ForceRefresh = ForceRefresh, diff --git a/tests/daemon-app/Daemon-app/Daemon-app.csproj b/tests/daemon-app/Daemon-app/Daemon-app.csproj new file mode 100644 index 000000000..505a16154 --- /dev/null +++ b/tests/daemon-app/Daemon-app/Daemon-app.csproj @@ -0,0 +1,27 @@ + + + + Exe + net462 + Daemon_app + enable + enable + 10.0 + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/tests/daemon-app/Daemon-app/Program.cs b/tests/daemon-app/Daemon-app/Program.cs new file mode 100644 index 000000000..170a14fbe --- /dev/null +++ b/tests/daemon-app/Daemon-app/Program.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Graph; +using Microsoft.Identity.Web; +using Microsoft.Identity.Web.TokenCacheProviders.Distributed; + +namespace daemon_console +{ + /// + /// This sample shows how to query the Microsoft Graph from a daemon application + /// which uses application permissions. + /// For more information see https://aka.ms/msal-net-client-credentials + /// + class Program + { + static void Main(string[] args) + { + try + { + RunAsync().GetAwaiter().GetResult(); + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(ex.Message); + Console.ResetColor(); + } + + Console.WriteLine("Press any key to exit"); + Console.ReadKey(); + } + + private static async Task RunAsync() + { + // Read the configuration from a file + IConfiguration Configuration; + var builder = new ConfigurationBuilder() + .SetBasePath(System.IO.Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + Configuration = builder.Build(); + + // Add services + ServiceCollection services = new ServiceCollection(); + + services.Configure(option => Configuration.Bind(option)); + services.AddMicrosoftGraph(); // or services.AddTokenAcquisition() if you don't need graph + + // Add a cache + services.AddDistributedTokenCaches(); + services.AddDistributedMemoryCache(); /* or SQL, Redis, ... */ + + var serviceProvider = services.BuildServiceProvider(); + + + + +#if WithTokenAcquisition + AuthenticationResult result = null; + // Get the token acquisition service + ITokenAcquisition? tokenAcquisition = serviceProvider.GetService(); + string scope = Configuration.GetValue("Scopes"); + result = await tokenAcquisition.GetAuthenticationResultForAppAsync(scope, null); +#else + GraphServiceClient graphServiceClient = serviceProvider.GetRequiredService(); + var users = await graphServiceClient.Users + .Request() + .WithAppOnly() + .GetAsync(); + + Console.WriteLine($"{users.Count} users"); +#endif + } + } +} diff --git a/tests/daemon-app/Daemon-app/appsettings.json b/tests/daemon-app/Daemon-app/appsettings.json new file mode 100644 index 000000000..12d277886 --- /dev/null +++ b/tests/daemon-app/Daemon-app/appsettings.json @@ -0,0 +1,12 @@ +{ + "Instance": "https://login.microsoftonline.com/", + "TenantId": "msidentitysamplestesting.onmicrosoft.com", + "ClientId": "6af093f3-b445-4b7a-beae-046864468ad6", + "ClientCertificates": [ + { + "SourceType": "KeyVault", + "KeyVaultUrl": "https://webappsapistests.vault.azure.net", + "KeyVaultCertificateName": "Self-Signed-5-5-22" + } + ] +} From 8bee620b53b6ee49cea874f5cf17960a137d4a36 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Thu, 17 Mar 2022 21:00:42 -0700 Subject: [PATCH 05/48] Add a daemon-net6 sample / test --- Microsoft.Identity.Web.sln | 17 ++++++- .../TokenAcquisitionAspnetCoreHost.cs | 11 ++++ .../Daemon-app-net60/Daemon-app.csproj | 27 ++++++++++ tests/daemon-app/Daemon-app-net60/Program.cs | 51 +++++++++++++++++++ .../Daemon-app-net60/appsettings.json | 12 +++++ 5 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 tests/daemon-app/Daemon-app-net60/Daemon-app.csproj create mode 100644 tests/daemon-app/Daemon-app-net60/Program.cs create mode 100644 tests/daemon-app/Daemon-app-net60/appsettings.json diff --git a/Microsoft.Identity.Web.sln b/Microsoft.Identity.Web.sln index 3905f2541..cc7602526 100644 --- a/Microsoft.Identity.Web.sln +++ b/Microsoft.Identity.Web.sln @@ -136,7 +136,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Cert EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Daemon", "Daemon", "{5D970909-98CF-415C-A598-D60B4B4AD8A9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Daemon-app", "tests\daemon-app\Daemon-app\Daemon-app.csproj", "{8C557C25-25C9-45B6-B2D3-77093893EBC5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daemon-app", "tests\daemon-app\Daemon-app\Daemon-app.csproj", "{8C557C25-25C9-45B6-B2D3-77093893EBC5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net461", "net461", "{A4BF732A-BED6-408E-805A-118CE2A692B6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net60", "net60", "{A61562E6-BB8A-4F97-B0D7-9A13E69753CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daemon-app", "tests\daemon-app\Daemon-app-net60\Daemon-app.csproj", "{23EEA681-C9CB-4358-93EB-13B9EE75D0CE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -308,6 +314,10 @@ Global {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C557C25-25C9-45B6-B2D3-77093893EBC5}.Release|Any CPU.Build.0 = Release|Any CPU + {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -363,7 +373,10 @@ Global {15B7F1B3-A438-4A42-BFFA-E3382543C037} = {D3A42D78-8A23-44EB-9A1F-63CF58B56AAC} {5B9AD363-6A27-4AF2-939E-B20E7C941ADF} = {20CDB9B2-D237-413A-8E76-A973597748B0} {5D970909-98CF-415C-A598-D60B4B4AD8A9} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} - {8C557C25-25C9-45B6-B2D3-77093893EBC5} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} + {8C557C25-25C9-45B6-B2D3-77093893EBC5} = {A4BF732A-BED6-408E-805A-118CE2A692B6} + {A4BF732A-BED6-408E-805A-118CE2A692B6} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} + {A61562E6-BB8A-4F97-B0D7-9A13E69753CB} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} + {23EEA681-C9CB-4358-93EB-13B9EE75D0CE} = {A61562E6-BB8A-4F97-B0D7-9A13E69753CB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4FA8C4C-3251-41CC-939B-7892F5798549} diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs b/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs index ea40df97e..1139b8b95 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisitionAspnetCoreHost.cs @@ -53,6 +53,17 @@ public TokenAcquisitionAspnetCoreHost( public MergedOptions GetOptions(string authenticationScheme) { var mergedOptions = _mergedOptionsMonitor.Get(authenticationScheme); + + // THIS IS A CHANGE: @jennyf19. Let's discuss if we want to merge this with + // ProcessOptionsForAnonymousControllers() below + // Case of an anonymous controller, no [Authorize] attribute will trigger the merge options + if (string.IsNullOrEmpty(mergedOptions.ClientId)) + { + var microsoftIdentityOptionsMonitor = _serviceProvider.GetService>(); + var microsoftIdentityOptions = microsoftIdentityOptionsMonitor?.Get(authenticationScheme); + MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions(microsoftIdentityOptions, mergedOptions); + } + if (!mergedOptions.MergedWithCca) { var ccaOptionsMonitor = _serviceProvider.GetService>(); diff --git a/tests/daemon-app/Daemon-app-net60/Daemon-app.csproj b/tests/daemon-app/Daemon-app-net60/Daemon-app.csproj new file mode 100644 index 000000000..2d68ece8d --- /dev/null +++ b/tests/daemon-app/Daemon-app-net60/Daemon-app.csproj @@ -0,0 +1,27 @@ + + + + Exe + net60 + Daemon_app + enable + enable + 10.0 + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/tests/daemon-app/Daemon-app-net60/Program.cs b/tests/daemon-app/Daemon-app-net60/Program.cs new file mode 100644 index 000000000..d5ccd5ccf --- /dev/null +++ b/tests/daemon-app/Daemon-app-net60/Program.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#define UseMicrosoftGraphSdk + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Identity.Web; +using Microsoft.Identity.Web.TokenCacheProviders.Distributed; +using GraphServiceClient = Microsoft.Graph.GraphServiceClient; + +namespace daemon_console +{ + /// + /// This sample shows how to query the Microsoft Graph from a daemon application + /// which uses application permissions. + /// For more information see https://aka.ms/msal-net-client-credentials + /// + class Program + { + static async Task Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + var services = builder.Services; + + services.Configure("OpenIdConnect", option => builder.Configuration.Bind(option)); + services.AddMicrosoftGraph(); // or services.AddTokenAcquisition() if you don't need graph + + // Add a cache + services.AddDistributedTokenCaches(); + services.AddDistributedMemoryCache(); /* or SQL, Redis, ... */ + + var app = builder.Build(); + +#if UseMicrosoftGraphSdk + GraphServiceClient graphServiceClient = app.Services.GetRequiredService(); + var users = await graphServiceClient.Users + .Request() + .WithAppOnly() + .GetAsync(); + Console.WriteLine($"{users.Count} users"); +#else + // Get the token acquisition service + ITokenAcquisition? tokenAcquisition = app.Services.GetService(); + var result = await tokenAcquisition.GetAuthenticationResultForAppAsync("https://graph.microsoft.com/.default", null, null); + Console.WriteLine($"Token expires on {result.ExpiresOn}"); + +#endif + } + } +} diff --git a/tests/daemon-app/Daemon-app-net60/appsettings.json b/tests/daemon-app/Daemon-app-net60/appsettings.json new file mode 100644 index 000000000..12d277886 --- /dev/null +++ b/tests/daemon-app/Daemon-app-net60/appsettings.json @@ -0,0 +1,12 @@ +{ + "Instance": "https://login.microsoftonline.com/", + "TenantId": "msidentitysamplestesting.onmicrosoft.com", + "ClientId": "6af093f3-b445-4b7a-beae-046864468ad6", + "ClientCertificates": [ + { + "SourceType": "KeyVault", + "KeyVaultUrl": "https://webappsapistests.vault.azure.net", + "KeyVaultCertificateName": "Self-Signed-5-5-22" + } + ] +} From f7e6e239ff6e91b03ed1995f00643c5feed4765a Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Sun, 20 Mar 2022 21:25:28 -0700 Subject: [PATCH 06/48] Moving ITokenAcquirer and TokenAcquisitionOptions to Microsoft.Identity.Web.TokenAcquisition.Abstractions --- Microsoft.Identity.Web.sln | 16 +++ .../ITokenAcquirer.cs | 12 +- ...y.Web.TokenAcquisition.Abstractions.csproj | 85 +++++++++++++ .../TokenAcquisitionOptions.cs | 5 + .../Microsoft.Identity.Web.csproj | 2 +- .../Microsoft.Identity.Web.xml | 114 ------------------ .../ServiceCollectionExtensions.cs | 2 + .../TokenAcquisition.cs | 12 +- tests/daemon-app/Daemon-app-net60/Program.cs | 6 +- tests/daemon-app/Daemon-app/Program.cs | 4 +- 10 files changed, 127 insertions(+), 131 deletions(-) rename src/{Microsoft.Identity.Web => Microsoft.Identity.Web.TokenAcquisition.Abstractions}/ITokenAcquirer.cs (79%) create mode 100644 src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj rename src/{Microsoft.Identity.Web => Microsoft.Identity.Web.TokenAcquisition.Abstractions}/TokenAcquisitionOptions.cs (96%) diff --git a/Microsoft.Identity.Web.sln b/Microsoft.Identity.Web.sln index cc7602526..48445e4dd 100644 --- a/Microsoft.Identity.Web.sln +++ b/Microsoft.Identity.Web.sln @@ -144,6 +144,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net60", "net60", "{A61562E6 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daemon-app", "tests\daemon-app\Daemon-app-net60\Daemon-app.csproj", "{23EEA681-C9CB-4358-93EB-13B9EE75D0CE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Asp.NET MVC", "Asp.NET MVC", "{7C04159B-BC84-4B8E-B2D8-692AEBA86433}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinWebApi", "tests\aspnet-mvc\OwinWebApi\OwinWebApi.csproj", "{E3611BED-B43A-40BD-A0A3-98AD65B5DAA4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.TokenAcquisition.Abstractions", "src\Microsoft.Identity.Web.TokenAcquisition.Abstractions\Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj", "{98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -318,6 +324,14 @@ Global {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {23EEA681-C9CB-4358-93EB-13B9EE75D0CE}.Release|Any CPU.Build.0 = Release|Any CPU + {E3611BED-B43A-40BD-A0A3-98AD65B5DAA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3611BED-B43A-40BD-A0A3-98AD65B5DAA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3611BED-B43A-40BD-A0A3-98AD65B5DAA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3611BED-B43A-40BD-A0A3-98AD65B5DAA4}.Release|Any CPU.Build.0 = Release|Any CPU + {98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -377,6 +391,8 @@ Global {A4BF732A-BED6-408E-805A-118CE2A692B6} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} {A61562E6-BB8A-4F97-B0D7-9A13E69753CB} = {5D970909-98CF-415C-A598-D60B4B4AD8A9} {23EEA681-C9CB-4358-93EB-13B9EE75D0CE} = {A61562E6-BB8A-4F97-B0D7-9A13E69753CB} + {7C04159B-BC84-4B8E-B2D8-692AEBA86433} = {9A51E6E0-A2B0-4D4D-9209-E73B8AD2B9C6} + {E3611BED-B43A-40BD-A0A3-98AD65B5DAA4} = {7C04159B-BC84-4B8E-B2D8-692AEBA86433} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4FA8C4C-3251-41CC-939B-7892F5798549} diff --git a/src/Microsoft.Identity.Web/ITokenAcquirer.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirer.cs similarity index 79% rename from src/Microsoft.Identity.Web/ITokenAcquirer.cs rename to src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirer.cs index 9520e3af4..cdd55065b 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquirer.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.Identity.Client; namespace Microsoft.Identity.Web @@ -16,13 +15,10 @@ public interface ITokenAcquirer { /// /// Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - /// for a downstream API on behalf of the user account for which the claims are provided in the - /// member of the controller's parameter. + /// for a downstream API on behalf of the user account for which the claims are provided in the current user /// /// Scopes to request for the downstream API to call. /// Options passed-in to create the token acquisition object which calls into MSAL .NET. - /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. /// Optional claims principal representing the user. If not provided, will use the signed-in /// user (in a web app), or the user for which the token was received (in a web API) /// cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. @@ -30,7 +26,6 @@ public interface ITokenAcquirer Task GetAuthenticationResultForUserAsync( IEnumerable scopes, TokenAcquisitionOptions? tokenAcquisitionOptions = null, - string? authenticationScheme = null, ClaimsPrincipal? user = null); /// @@ -43,12 +38,9 @@ Task GetAuthenticationResultForUserAsync( /// in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use /// several calls to get tokens for other resources). /// Options passed-in to create the token acquisition object which calls into MSAL .NET. - /// Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - /// if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. /// An authentication result for the app itself, based on its scopes. Task GetAuthenticationResultForAppAsync( string scope, - TokenAcquisitionOptions? tokenAcquisitionOptions = null, - string? authenticationScheme = null); + TokenAcquisitionOptions? tokenAcquisitionOptions = null); } } diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj new file mode 100644 index 000000000..2349709ac --- /dev/null +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj @@ -0,0 +1,85 @@ + + + + + 1.0.0-localbuild + + $(ClientSemVer) + + $(DefineConstants);WEB + true + + Microsoft Identity Web Token Acquisition Abstractions + Microsoft + Microsoft Corporation + Microsoft Identity Web Acquisition Abstractions + + Interface for higher level API for confidential client applications. + + © Microsoft Corporation. All rights reserved. + MIT + https://github.com/AzureAD/microsoft-identity-web + https://github.com/AzureAD/microsoft-identity-web + The release notes are available at https://github.com/AzureAD/microsoft-identity-web/releases and the roadmap at https://github.com/AzureAD/microsoft-identity-web/wiki#roadmap + Microsoft Identity Web;Microsoft identity platform;Microsoft.Identity.Web;.NET;ASP.NET Core;Web App;Web API;B2C;Azure Active Directory;AAD;Identity;Authentication;Authorization + {98F57CC8-01A0-49F3-B859-DDC4F8F5CD2F} + + + true + true + + true + snupkg + + + + + + + + + + + + + True + + + + + + + + + + net45;netstandard13 + true + ../../build/MSAL.snk + true + enable + 8.0 + + + + + false + ..\..\.sonarlint\azuread_microsoft-identity-webcsharp.ruleset + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + true + true + true + snupkg + true + + + diff --git a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs similarity index 96% rename from src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs rename to src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs index 8ec87e7f6..5562d5214 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs @@ -26,6 +26,11 @@ public class TokenAcquisitionOptions /// public string? UserFlow { get; set; } + /// + /// Requires a particular authentication scheme / settings + /// + public string? AuthenticationScheme{ get; set; } + /// /// Sets the correlation id to be used in the authentication request /// to the /token endpoint. diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj index b1333ae34..624df4665 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj @@ -118,7 +118,6 @@ - @@ -157,6 +156,7 @@ + diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index cfe4d9a67..753baa0ac 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -1203,41 +1203,6 @@ Issuer associated with the OIDC endpoint. - - - Interface for the token acquisition service (encapsulating MSAL.NET). - - - - - Typically used from an ASP.NET Core web app or web API controller. This method gets an access token - for a downstream API on behalf of the user account for which the claims are provided in the - member of the controller's parameter. - - Scopes to request for the downstream API to call. - Options passed-in to create the token acquisition object which calls into MSAL .NET. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web APIs. - Optional claims principal representing the user. If not provided, will use the signed-in - user (in a web app), or the user for which the token was received (in a web API) - cases where a given account is a guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in. - An to call on behalf of the user, the downstream API characterized by its scopes. - - - - Acquires an authentication result from the authority configured in the app, for the confidential client itself (not on behalf of a user) - using the client credentials flow. See https://aka.ms/msal-net-client-credentials. - - The scope requested to access a protected API. For this flow (client credentials), the scope - should be of the form "{ResourceIdUri/.default}" for instance https://management.azure.net/.default or, for Microsoft - Graph, https://graph.microsoft.com/.default as the requested scopes are defined statically with the application registration - in the portal, and cannot be overridden in the application, as you can request a token for only one resource at a time (use - several calls to get tokens for other resources). - Options passed-in to create the token acquisition object which calls into MSAL .NET. - Authentication scheme. If null, will use OpenIdConnectDefault.AuthenticationScheme - if called from a web app, and JwtBearerDefault.AuthenticationScheme if called from a web API. - An authentication result for the app itself, based on its scopes. - Interface for the token acquisition service (encapsulating MSAL.NET). @@ -2475,85 +2440,6 @@ - - - Options passed-in to create the token acquisition object which calls into MSAL .NET. - - - - - Enables to override the tenant/account for the same identity. This is useful in multi-tenant apps - in the cases where a given account is a guest in other tenants, and you want to acquire tokens - for a specific tenant. - - Can be the tenant ID or domain name. - - - - Uses a particular user flow (in the case of AzureAD B2C) - - - - - Sets the correlation id to be used in the authentication request - to the /token endpoint. - - - - - Sets Extra Query Parameters for the query string in the HTTP authentication request. - - - - - A string with one or multiple claims to request. - Normally used with Conditional Access. - - - - - Specifies if the token request will ignore the access token in the token cache - and will attempt to acquire a new access token. - If true, the request will ignore the token cache. The default is false. - Use this option with care and only when needed, for instance, if you know that conditional access policies have changed, - for it induces performance degradation, as the token cache is not utilized. - - - - - Modifies the token acquisition request so that the acquired token is a Proof of Possession token (PoP), - rather than a Bearer token. - PoP tokens are similar to Bearer tokens, but are bound to the HTTP request and to a cryptographic key, - which MSAL can manage. See https://aka.ms/msal-net-pop. - - - - - Cancellation token to be used when calling the token acquisition methods. - - - - - Key used for long running web APIs that need to call downstream web - APIs on behalf of the user. Can be null, if you are not developing a long - running web API, if you want - Microsoft.Identity.Web to allocate a session key for you, or your own string - if you want to associate the session with some information you have externally - (for instance a Microsoft Graph hook identifier). - - - - - Value that can be used for so that - MSAL.NET allocates the long running web api session key for the developer. - - - - - Clone the options (to be able to override them). - - A clone of the options. - An implementation of token cache for confidential clients backed by an HTTP session. diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index fe3cbe13e..7cd803596 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -63,6 +63,7 @@ public static IServiceCollection AddTokenAcquisition( if (isTokenAcquisitionSingleton) { services.AddSingleton(); + services.AddSingleton(s => (ITokenAcquirer)s.GetRequiredService()); #if !NET472 && !NET462 services.AddHttpContextAccessor(); services.AddSingleton(); @@ -74,6 +75,7 @@ public static IServiceCollection AddTokenAcquisition( else { services.AddScoped(); + services.AddScoped(s => (ITokenAcquirer)s.GetRequiredService()); #if !NET472 && !NET462 services.AddHttpContextAccessor(); services.AddScoped(); diff --git a/src/Microsoft.Identity.Web/TokenAcquisition.cs b/src/Microsoft.Identity.Web/TokenAcquisition.cs index dbcdd143c..e32bb7943 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisition.cs @@ -25,7 +25,7 @@ namespace Microsoft.Identity.Web /// /// Token acquisition service. /// - internal partial class TokenAcquisition : ITokenAcquisition, ITokenAcquisitionInternal + internal partial class TokenAcquisition : ITokenAcquisition, ITokenAcquisitionInternal, ITokenAcquirer { #if NET472 || NET462 class OAuthConstants @@ -892,5 +892,15 @@ public string GetEffectiveAuthenticationScheme(string? authenticationScheme) { return _tokenAcquisitionHost.GetEffectiveAuthenticationScheme(authenticationScheme); } + + Task ITokenAcquirer.GetAuthenticationResultForUserAsync(IEnumerable scopes, TokenAcquisitionOptions? tokenAcquisitionOptions, ClaimsPrincipal? user) + { + return GetAuthenticationResultForUserAsync(scopes, tokenAcquisitionOptions?.AuthenticationScheme, tokenAcquisitionOptions?.Tenant, tokenAcquisitionOptions?.UserFlow, user, tokenAcquisitionOptions); + } + + Task ITokenAcquirer.GetAuthenticationResultForAppAsync(string scope, TokenAcquisitionOptions? tokenAcquisitionOptions) + { + return GetAuthenticationResultForAppAsync(scope, tokenAcquisitionOptions?.AuthenticationScheme, tokenAcquisitionOptions?.Tenant, tokenAcquisitionOptions); + } } } diff --git a/tests/daemon-app/Daemon-app-net60/Program.cs b/tests/daemon-app/Daemon-app-net60/Program.cs index d5ccd5ccf..eaa38087d 100644 --- a/tests/daemon-app/Daemon-app-net60/Program.cs +++ b/tests/daemon-app/Daemon-app-net60/Program.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#define UseMicrosoftGraphSdk +//#define UseMicrosoftGraphSdk using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; @@ -41,8 +41,8 @@ static async Task Main(string[] args) Console.WriteLine($"{users.Count} users"); #else // Get the token acquisition service - ITokenAcquisition? tokenAcquisition = app.Services.GetService(); - var result = await tokenAcquisition.GetAuthenticationResultForAppAsync("https://graph.microsoft.com/.default", null, null); + ITokenAcquirer tokenAcquirer = app.Services.GetRequiredService(); + var result = await tokenAcquirer.GetAuthenticationResultForAppAsync("https://graph.microsoft.com/.default"); Console.WriteLine($"Token expires on {result.ExpiresOn}"); #endif diff --git a/tests/daemon-app/Daemon-app/Program.cs b/tests/daemon-app/Daemon-app/Program.cs index 1c72f3731..61cde4bf1 100644 --- a/tests/daemon-app/Daemon-app/Program.cs +++ b/tests/daemon-app/Daemon-app/Program.cs @@ -42,9 +42,9 @@ static async Task Main(string[] args) Console.WriteLine($"{users.Count} users"); #else // Get the token acquisition service - ITokenAcquisition? tokenAcquisition = serviceProvider.GetService(); + ITokenAcquirer tokenAcquirer = serviceProvider.GetRequiredService(); string scope = configuration.GetValue("Scopes"); - var result = await tokenAcquisition.GetAuthenticationResultForAppAsync("https://graph.microsoft.com/.default", null); + var result = await tokenAcquirer.GetAuthenticationResultForAppAsync("https://graph.microsoft.com/.default"); Console.WriteLine($"Token expires on {result.ExpiresOn}"); #endif From 7af0936d29afda9430f255287be7e259dd2b7882 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Sat, 16 Apr 2022 11:06:37 -0700 Subject: [PATCH 07/48] Update --- .../ITokenAcquirerFactory.cs | 10 ++++++++++ ...t.Identity.Web.TokenAcquisition.Abstractions.csproj | 2 +- .../TokenAcquirerFactory.cs | 7 +++++++ .../TokenAcquisitionOptions.cs | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs create mode 100644 src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs new file mode 100644 index 000000000..81dbac488 --- /dev/null +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs @@ -0,0 +1,10 @@ +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Identity.Web +{ + public interface ITokenAcquirerFactory + { + ITokenAcquirer GetTokenAcquirer(string authority, string region, string clientId, X509Certificate2 certificate); + ITokenAcquirer GetTokenAcquirer(string authenticationScheme); + } +} diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj index 2349709ac..a9c0d89a1 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj @@ -52,7 +52,7 @@ - net45;netstandard13 + net45;netstandard13;net472 true ../../build/MSAL.snk true diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs new file mode 100644 index 000000000..db2de22a0 --- /dev/null +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Identity.Web +{ + public class TokenAcquirerFactory + { + static public ITokenAcquirerFactory Instance { get; } + } +} diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs index 5562d5214..3f53a07c1 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs @@ -96,12 +96,14 @@ public TokenAcquisitionOptions Clone() { Tenant = Tenant, UserFlow = UserFlow, + AuthenticationScheme = AuthenticationScheme, CorrelationId = CorrelationId, ExtraQueryParameters = ExtraQueryParameters, ForceRefresh = ForceRefresh, Claims = Claims, PoPConfiguration = PoPConfiguration, CancellationToken = CancellationToken, + LongRunningWebApiSessionKey = LongRunningWebApiSessionKey, }; } } From 6a7242639151d2b551b89142264b4310ebe5c5c5 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Sat, 16 Apr 2022 11:06:37 -0700 Subject: [PATCH 08/48] Update --- .../ITokenAcquirerFactory.cs | 10 + ...y.Web.TokenAcquisition.Abstractions.csproj | 2 +- .../TokenAcquirerFactory.cs | 7 + .../TokenAcquisitionOptions.cs | 2 + .../AcquireTokenForAppIntegrationTests.cs | 1 - .../OwinWebApi/.config/dotnet-tools.json | 12 + .../OwinWebApi/App_Start/BundleConfig.cs | 27 + .../OwinWebApi/App_Start/FilterConfig.cs | 13 + .../OwinWebApi/App_Start/RouteConfig.cs | 23 + .../OwinWebApi/App_Start/Startup.Auth.cs | 28 + .../OwinWebApi/App_Start/WebApiConfig.cs | 24 + .../HelpPage/ApiDescriptionExtensions.cs | 39 + .../HelpPage/App_Start/HelpPageConfig.cs | 113 + .../HelpPage/Controllers/HelpController.cs | 63 + .../OwinWebApi/Areas/HelpPage/HelpPage.css | 134 + .../HelpPage/HelpPageAreaRegistration.cs | 26 + .../HelpPageConfigurationExtensions.cs | 467 + .../CollectionModelDescription.cs | 7 + .../ComplexTypeModelDescription.cs | 14 + .../DictionaryModelDescription.cs | 6 + .../EnumTypeModelDescription.cs | 15 + .../ModelDescriptions/EnumValueDescription.cs | 11 + .../IModelDocumentationProvider.cs | 12 + .../KeyValuePairModelDescription.cs | 9 + .../ModelDescriptions/ModelDescription.cs | 16 + .../ModelDescriptionGenerator.cs | 451 + .../ModelDescriptions/ModelNameAttribute.cs | 18 + .../ModelDescriptions/ModelNameHelper.cs | 36 + .../ModelDescriptions/ParameterAnnotation.cs | 11 + .../ModelDescriptions/ParameterDescription.cs | 21 + .../SimpleTypeModelDescription.cs | 6 + .../Areas/HelpPage/Models/HelpPageApiModel.cs | 108 + .../HelpPageSampleGenerator.cs | 444 + .../SampleGeneration/HelpPageSampleKey.cs | 172 + .../HelpPage/SampleGeneration/ImageSample.cs | 41 + .../SampleGeneration/InvalidSample.cs | 37 + .../SampleGeneration/ObjectGenerator.cs | 456 + .../SampleGeneration/SampleDirection.cs | 11 + .../HelpPage/SampleGeneration/TextSample.cs | 37 + .../Areas/HelpPage/Views/Help/Api.cshtml | 22 + .../Help/DisplayTemplates/ApiGroup.cshtml | 41 + .../CollectionModelDescription.cshtml | 6 + .../ComplexTypeModelDescription.cshtml | 3 + .../DictionaryModelDescription.cshtml | 4 + .../EnumTypeModelDescription.cshtml | 24 + .../DisplayTemplates/HelpPageApiModel.cshtml | 67 + .../Help/DisplayTemplates/ImageSample.cshtml | 4 + .../DisplayTemplates/InvalidSample.cshtml | 13 + .../KeyValuePairModelDescription.cshtml | 4 + .../ModelDescriptionLink.cshtml | 26 + .../Help/DisplayTemplates/Parameters.cshtml | 48 + .../Help/DisplayTemplates/Samples.cshtml | 30 + .../SimpleTypeModelDescription.cshtml | 3 + .../Help/DisplayTemplates/TextSample.cshtml | 6 + .../Areas/HelpPage/Views/Help/Index.cshtml | 38 + .../HelpPage/Views/Help/ResourceModel.cshtml | 19 + .../HelpPage/Views/Shared/_Layout.cshtml | 12 + .../Areas/HelpPage/Views/Web.config | 41 + .../Areas/HelpPage/Views/_ViewStart.cshtml | 4 + .../HelpPage/XmlDocumentationProvider.cs | 161 + tests/aspnet-mvc/OwinWebApi/Content/Site.css | 17 + .../OwinWebApi/Content/bootstrap-theme.css | 587 + .../Content/bootstrap-theme.css.map | 1 + .../Content/bootstrap-theme.min.css | 6 + .../Content/bootstrap-theme.min.css.map | 1 + .../OwinWebApi/Content/bootstrap.css | 6834 ++++++++++ .../OwinWebApi/Content/bootstrap.css.map | 1 + .../OwinWebApi/Content/bootstrap.min.css | 6 + .../OwinWebApi/Content/bootstrap.min.css.map | 1 + .../OwinWebApi/Controllers/HomeController.cs | 18 + .../Controllers/ValuesController.cs | 45 + tests/aspnet-mvc/OwinWebApi/Global.asax | 1 + tests/aspnet-mvc/OwinWebApi/Global.asax.cs | 23 + tests/aspnet-mvc/OwinWebApi/OwinWebApi.csproj | 346 + .../OwinWebApi/Properties/AssemblyInfo.cs | 35 + .../OwinWebApi/Scripts/bootstrap.js | 2580 ++++ .../OwinWebApi/Scripts/bootstrap.min.js | 6 + .../Scripts/jquery-3.4.1.intellisense.js | 2670 ++++ .../OwinWebApi/Scripts/jquery-3.4.1.js | 10598 ++++++++++++++++ .../OwinWebApi/Scripts/jquery-3.4.1.min.js | 2 + .../OwinWebApi/Scripts/jquery-3.4.1.min.map | 1 + .../OwinWebApi/Scripts/jquery-3.4.1.slim.js | 8495 +++++++++++++ .../Scripts/jquery-3.4.1.slim.min.js | 2 + .../Scripts/jquery-3.4.1.slim.min.map | 1 + .../OwinWebApi/Scripts/modernizr-2.8.3.js | 1406 ++ tests/aspnet-mvc/OwinWebApi/Startup.cs | 12 + .../OwinWebApi/Views/Home/Index.cshtml | 24 + .../OwinWebApi/Views/Shared/Error.cshtml | 13 + .../OwinWebApi/Views/Shared/_Layout.cshtml | 41 + tests/aspnet-mvc/OwinWebApi/Views/Web.config | 43 + .../OwinWebApi/Views/_ViewStart.cshtml | 3 + tests/aspnet-mvc/OwinWebApi/Web.Debug.config | 30 + .../aspnet-mvc/OwinWebApi/Web.Release.config | 31 + tests/aspnet-mvc/OwinWebApi/Web.config | 79 + tests/aspnet-mvc/OwinWebApi/favicon.ico | Bin 0 -> 32038 bytes .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../fonts/glyphicons-halflings-regular.svg | 288 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes tests/aspnet-mvc/OwinWebApi/packages.config | 35 + 101 files changed, 37786 insertions(+), 2 deletions(-) create mode 100644 src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs create mode 100644 src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/.config/dotnet-tools.json create mode 100644 tests/aspnet-mvc/OwinWebApi/App_Start/BundleConfig.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/App_Start/FilterConfig.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/App_Start/RouteConfig.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/App_Start/Startup.Auth.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/App_Start/WebApiConfig.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ApiDescriptionExtensions.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/App_Start/HelpPageConfig.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Controllers/HelpController.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPage.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageAreaRegistration.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageConfigurationExtensions.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Models/HelpPageApiModel.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ImageSample.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/InvalidSample.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/SampleDirection.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/TextSample.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Api.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Index.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/ResourceModel.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Shared/_Layout.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Web.config create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/_ViewStart.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/XmlDocumentationProvider.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/Site.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap.min.css create mode 100644 tests/aspnet-mvc/OwinWebApi/Content/bootstrap.min.css.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Controllers/HomeController.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Controllers/ValuesController.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Global.asax create mode 100644 tests/aspnet-mvc/OwinWebApi/Global.asax.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/OwinWebApi.csproj create mode 100644 tests/aspnet-mvc/OwinWebApi/Properties/AssemblyInfo.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/bootstrap.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/bootstrap.min.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.intellisense.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.min.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.min.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.slim.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.slim.min.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/jquery-3.4.1.slim.min.map create mode 100644 tests/aspnet-mvc/OwinWebApi/Scripts/modernizr-2.8.3.js create mode 100644 tests/aspnet-mvc/OwinWebApi/Startup.cs create mode 100644 tests/aspnet-mvc/OwinWebApi/Views/Home/Index.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Views/Shared/Error.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Views/Shared/_Layout.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Views/Web.config create mode 100644 tests/aspnet-mvc/OwinWebApi/Views/_ViewStart.cshtml create mode 100644 tests/aspnet-mvc/OwinWebApi/Web.Debug.config create mode 100644 tests/aspnet-mvc/OwinWebApi/Web.Release.config create mode 100644 tests/aspnet-mvc/OwinWebApi/Web.config create mode 100644 tests/aspnet-mvc/OwinWebApi/favicon.ico create mode 100644 tests/aspnet-mvc/OwinWebApi/fonts/glyphicons-halflings-regular.eot create mode 100644 tests/aspnet-mvc/OwinWebApi/fonts/glyphicons-halflings-regular.svg create mode 100644 tests/aspnet-mvc/OwinWebApi/fonts/glyphicons-halflings-regular.ttf create mode 100644 tests/aspnet-mvc/OwinWebApi/fonts/glyphicons-halflings-regular.woff create mode 100644 tests/aspnet-mvc/OwinWebApi/fonts/glyphicons-halflings-regular.woff2 create mode 100644 tests/aspnet-mvc/OwinWebApi/packages.config diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs new file mode 100644 index 000000000..81dbac488 --- /dev/null +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/ITokenAcquirerFactory.cs @@ -0,0 +1,10 @@ +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Identity.Web +{ + public interface ITokenAcquirerFactory + { + ITokenAcquirer GetTokenAcquirer(string authority, string region, string clientId, X509Certificate2 certificate); + ITokenAcquirer GetTokenAcquirer(string authenticationScheme); + } +} diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj index 2349709ac..a9c0d89a1 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/Microsoft.Identity.Web.TokenAcquisition.Abstractions.csproj @@ -52,7 +52,7 @@ - net45;netstandard13 + net45;netstandard13;net472 true ../../build/MSAL.snk true diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs new file mode 100644 index 000000000..db2de22a0 --- /dev/null +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquirerFactory.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Identity.Web +{ + public class TokenAcquirerFactory + { + static public ITokenAcquirerFactory Instance { get; } + } +} diff --git a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs index 5562d5214..3f53a07c1 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition.Abstractions/TokenAcquisitionOptions.cs @@ -96,12 +96,14 @@ public TokenAcquisitionOptions Clone() { Tenant = Tenant, UserFlow = UserFlow, + AuthenticationScheme = AuthenticationScheme, CorrelationId = CorrelationId, ExtraQueryParameters = ExtraQueryParameters, ForceRefresh = ForceRefresh, Claims = Claims, PoPConfiguration = PoPConfiguration, CancellationToken = CancellationToken, + LongRunningWebApiSessionKey = LongRunningWebApiSessionKey, }; } } diff --git a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs index a014a2b23..83baf7a71 100644 --- a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs +++ b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs @@ -296,7 +296,6 @@ private void BuildTheRequiredServices() services.AddHttpClient(); _provider = services.BuildServiceProvider(); } - } #endif //FROM_GITHUB_ACTION } diff --git a/tests/aspnet-mvc/OwinWebApi/.config/dotnet-tools.json b/tests/aspnet-mvc/OwinWebApi/.config/dotnet-tools.json new file mode 100644 index 000000000..fed33b37a --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "microsoft.dotnet-msidentity": { + "version": "1.0.1", + "commands": [ + "dotnet-msidentity" + ] + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/App_Start/BundleConfig.cs b/tests/aspnet-mvc/OwinWebApi/App_Start/BundleConfig.cs new file mode 100644 index 000000000..10dbf81b5 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/App_Start/BundleConfig.cs @@ -0,0 +1,27 @@ +using System.Web; +using System.Web.Optimization; + +namespace OwinWebApi +{ + public class BundleConfig + { + // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 + public static void RegisterBundles(BundleCollection bundles) + { + bundles.Add(new ScriptBundle("~/bundles/jquery").Include( + "~/Scripts/jquery-{version}.js")); + + // Use the development version of Modernizr to develop with and learn from. Then, when you're + // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. + bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( + "~/Scripts/modernizr-*")); + + bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( + "~/Scripts/bootstrap.js")); + + bundles.Add(new StyleBundle("~/Content/css").Include( + "~/Content/bootstrap.css", + "~/Content/site.css")); + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/App_Start/FilterConfig.cs b/tests/aspnet-mvc/OwinWebApi/App_Start/FilterConfig.cs new file mode 100644 index 000000000..df2cc0b34 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/App_Start/FilterConfig.cs @@ -0,0 +1,13 @@ +using System.Web; +using System.Web.Mvc; + +namespace OwinWebApi +{ + public class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/App_Start/RouteConfig.cs b/tests/aspnet-mvc/OwinWebApi/App_Start/RouteConfig.cs new file mode 100644 index 000000000..702f7210d --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/App_Start/RouteConfig.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; + +namespace OwinWebApi +{ + public class RouteConfig + { + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/App_Start/Startup.Auth.cs b/tests/aspnet-mvc/OwinWebApi/App_Start/Startup.Auth.cs new file mode 100644 index 000000000..74288d9f3 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/App_Start/Startup.Auth.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IdentityModel.Tokens; +using System.Linq; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.ActiveDirectory; +using Owin; + +namespace OwinWebApi +{ + public partial class Startup + { + // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864 + public void ConfigureAuth(IAppBuilder app) + { + app.UseWindowsAzureActiveDirectoryBearerAuthentication( + new WindowsAzureActiveDirectoryBearerAuthenticationOptions + { + Tenant = ConfigurationManager.AppSettings["ida:Tenant"], + TokenValidationParameters = new TokenValidationParameters { + ValidAudience = ConfigurationManager.AppSettings["ida:Audience"], + }, + }); + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/App_Start/WebApiConfig.cs b/tests/aspnet-mvc/OwinWebApi/App_Start/WebApiConfig.cs new file mode 100644 index 000000000..341665351 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/App_Start/WebApiConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http; + +namespace OwinWebApi +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Web API configuration and services + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ApiDescriptionExtensions.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ApiDescriptionExtensions.cs new file mode 100644 index 000000000..d575efd9b --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ApiDescriptionExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.Text; +using System.Web; +using System.Web.Http.Description; + +namespace OwinWebApi.Areas.HelpPage +{ + public static class ApiDescriptionExtensions + { + /// + /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" + /// + /// The . + /// The ID as a string. + public static string GetFriendlyId(this ApiDescription description) + { + string path = description.RelativePath; + string[] urlParts = path.Split('?'); + string localPath = urlParts[0]; + string queryKeyString = null; + if (urlParts.Length > 1) + { + string query = urlParts[1]; + string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys; + queryKeyString = String.Join("_", queryKeys); + } + + StringBuilder friendlyPath = new StringBuilder(); + friendlyPath.AppendFormat("{0}-{1}", + description.HttpMethod.Method, + localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)); + if (queryKeyString != null) + { + friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-')); + } + return friendlyPath.ToString(); + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/App_Start/HelpPageConfig.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/App_Start/HelpPageConfig.cs new file mode 100644 index 000000000..466bcf542 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/App_Start/HelpPageConfig.cs @@ -0,0 +1,113 @@ +// Uncomment the following to provide samples for PageResult. Must also add the Microsoft.AspNet.WebApi.OData +// package to your project. +////#define Handle_PageResultOfT + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net.Http.Headers; +using System.Reflection; +using System.Web; +using System.Web.Http; +#if Handle_PageResultOfT +using System.Web.Http.OData; +#endif + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// Use this class to customize the Help Page. + /// For example you can set a custom to supply the documentation + /// or you can provide the samples for the requests/responses. + /// + public static class HelpPageConfig + { + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", + MessageId = "OwinWebApi.Areas.HelpPage.TextSample.#ctor(System.String)", + Justification = "End users may choose to merge this string with existing localized resources.")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", + MessageId = "bsonspec", + Justification = "Part of a URI.")] + public static void Register(HttpConfiguration config) + { + //// Uncomment the following to use the documentation from XML documentation file. + //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))); + + //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. + //// Also, the string arrays will be used for IEnumerable. The sample objects will be serialized into different media type + //// formats by the available formatters. + //config.SetSampleObjects(new Dictionary + //{ + // {typeof(string), "sample string"}, + // {typeof(IEnumerable), new string[]{"sample 1", "sample 2"}} + //}); + + // Extend the following to provide factories for types not handled automatically (those lacking parameterless + // constructors) or for which you prefer to use non-default property values. Line below provides a fallback + // since automatic handling will fail and GeneratePageResult handles only a single type. +#if Handle_PageResultOfT + config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult); +#endif + + // Extend the following to use a preset object directly as the sample for all actions that support a media + // type, regardless of the body parameter or return type. The lines below avoid display of binary content. + // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. + config.SetSampleForMediaType( + new TextSample("Binary JSON content. See http://bsonspec.org for details."), + new MediaTypeHeaderValue("application/bson")); + + //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format + //// and have IEnumerable as the body parameter or return type. + //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable)); + + //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" + //// and action named "Put". + //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put"); + + //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" + //// on the controller named "Values" and action named "Get" with parameter "id". + //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id"); + + //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent. + //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter. + //config.SetActualRequestType(typeof(string), "Values", "Get"); + + //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent. + //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string. + //config.SetActualResponseType(typeof(string), "Values", "Post"); + } + +#if Handle_PageResultOfT + private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type) + { + if (type.IsGenericType) + { + Type openGenericType = type.GetGenericTypeDefinition(); + if (openGenericType == typeof(PageResult<>)) + { + // Get the T in PageResult + Type[] typeParameters = type.GetGenericArguments(); + Debug.Assert(typeParameters.Length == 1); + + // Create an enumeration to pass as the first parameter to the PageResult constuctor + Type itemsType = typeof(List<>).MakeGenericType(typeParameters); + object items = sampleGenerator.GetSampleObject(itemsType); + + // Fill in the other information needed to invoke the PageResult constuctor + Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), }; + object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, }; + + // Call PageResult(IEnumerable items, Uri nextPageLink, long? count) constructor + ConstructorInfo constructor = type.GetConstructor(parameterTypes); + return constructor.Invoke(parameters); + } + } + + return null; + } +#endif + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Controllers/HelpController.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Controllers/HelpController.cs new file mode 100644 index 000000000..5de9576f3 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Controllers/HelpController.cs @@ -0,0 +1,63 @@ +using System; +using System.Web.Http; +using System.Web.Mvc; +using OwinWebApi.Areas.HelpPage.ModelDescriptions; +using OwinWebApi.Areas.HelpPage.Models; + +namespace OwinWebApi.Areas.HelpPage.Controllers +{ + /// + /// The controller that will handle requests for the help page. + /// + public class HelpController : Controller + { + private const string ErrorViewName = "Error"; + + public HelpController() + : this(GlobalConfiguration.Configuration) + { + } + + public HelpController(HttpConfiguration config) + { + Configuration = config; + } + + public HttpConfiguration Configuration { get; private set; } + + public ActionResult Index() + { + ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider(); + return View(Configuration.Services.GetApiExplorer().ApiDescriptions); + } + + public ActionResult Api(string apiId) + { + if (!String.IsNullOrEmpty(apiId)) + { + HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); + if (apiModel != null) + { + return View(apiModel); + } + } + + return View(ErrorViewName); + } + + public ActionResult ResourceModel(string modelName) + { + if (!String.IsNullOrEmpty(modelName)) + { + ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator(); + ModelDescription modelDescription; + if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription)) + { + return View(modelDescription); + } + } + + return View(ErrorViewName); + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPage.css b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPage.css new file mode 100644 index 000000000..aff223033 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPage.css @@ -0,0 +1,134 @@ +.help-page h1, +.help-page .h1, +.help-page h2, +.help-page .h2, +.help-page h3, +.help-page .h3, +#body.help-page, +.help-page-table th, +.help-page-table pre, +.help-page-table p { + font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; +} + +.help-page pre.wrapped { + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + white-space: pre-wrap; +} + +.help-page .warning-message-container { + margin-top: 20px; + padding: 0 10px; + color: #525252; + background: #EFDCA9; + border: 1px solid #CCCCCC; +} + +.help-page-table { + width: 100%; + border-collapse: collapse; + text-align: left; + margin: 0px 0px 20px 0px; + border-top: 1px solid #D4D4D4; +} + +.help-page-table th { + text-align: left; + font-weight: bold; + border-bottom: 1px solid #D4D4D4; + padding: 5px 6px 5px 6px; +} + +.help-page-table td { + border-bottom: 1px solid #D4D4D4; + padding: 10px 8px 10px 8px; + vertical-align: top; +} + +.help-page-table pre, +.help-page-table p { + margin: 0px; + padding: 0px; + font-family: inherit; + font-size: 100%; +} + +.help-page-table tbody tr:hover td { + background-color: #F3F3F3; +} + +.help-page a:hover { + background-color: transparent; +} + +.help-page .sample-header { + border: 2px solid #D4D4D4; + background: #00497E; + color: #FFFFFF; + padding: 8px 15px; + border-bottom: none; + display: inline-block; + margin: 10px 0px 0px 0px; +} + +.help-page .sample-content { + display: block; + border-width: 0; + padding: 15px 20px; + background: #FFFFFF; + border: 2px solid #D4D4D4; + margin: 0px 0px 10px 0px; +} + +.help-page .api-name { + width: 40%; +} + +.help-page .api-documentation { + width: 60%; +} + +.help-page .parameter-name { + width: 20%; +} + +.help-page .parameter-documentation { + width: 40%; +} + +.help-page .parameter-type { + width: 20%; +} + +.help-page .parameter-annotations { + width: 20%; +} + +.help-page h1, +.help-page .h1 { + font-size: 36px; + line-height: normal; +} + +.help-page h2, +.help-page .h2 { + font-size: 24px; +} + +.help-page h3, +.help-page .h3 { + font-size: 20px; +} + +#body.help-page { + font-size: 14px; + line-height: 143%; + color: #333; +} + +.help-page a { + color: #0000EE; + text-decoration: none; +} diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageAreaRegistration.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageAreaRegistration.cs new file mode 100644 index 000000000..aa6b568fa --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageAreaRegistration.cs @@ -0,0 +1,26 @@ +using System.Web.Http; +using System.Web.Mvc; + +namespace OwinWebApi.Areas.HelpPage +{ + public class HelpPageAreaRegistration : AreaRegistration + { + public override string AreaName + { + get + { + return "HelpPage"; + } + } + + public override void RegisterArea(AreaRegistrationContext context) + { + context.MapRoute( + "HelpPage_Default", + "Help/{action}/{apiId}", + new { controller = "Help", action = "Index", apiId = UrlParameter.Optional }); + + HelpPageConfig.Register(GlobalConfiguration.Configuration); + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageConfigurationExtensions.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageConfigurationExtensions.cs new file mode 100644 index 000000000..bfc7df483 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/HelpPageConfigurationExtensions.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Description; +using OwinWebApi.Areas.HelpPage.ModelDescriptions; +using OwinWebApi.Areas.HelpPage.Models; + +namespace OwinWebApi.Areas.HelpPage +{ + public static class HelpPageConfigurationExtensions + { + private const string ApiModelPrefix = "MS_HelpPageApiModel_"; + + /// + /// Sets the documentation provider for help page. + /// + /// The . + /// The documentation provider. + public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider) + { + config.Services.Replace(typeof(IDocumentationProvider), documentationProvider); + } + + /// + /// Sets the objects that will be used by the formatters to produce sample requests/responses. + /// + /// The . + /// The sample objects. + public static void SetSampleObjects(this HttpConfiguration config, IDictionary sampleObjects) + { + config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects; + } + + /// + /// Sets the sample request directly for the specified media type and action. + /// + /// The . + /// The sample request. + /// The media type. + /// Name of the controller. + /// Name of the action. + public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample); + } + + /// + /// Sets the sample request directly for the specified media type and action with parameters. + /// + /// The . + /// The sample request. + /// The media type. + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample); + } + + /// + /// Sets the sample request directly for the specified media type of the action. + /// + /// The . + /// The sample response. + /// The media type. + /// Name of the controller. + /// Name of the action. + public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample); + } + + /// + /// Sets the sample response directly for the specified media type of the action with specific parameters. + /// + /// The . + /// The sample response. + /// The media type. + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample); + } + + /// + /// Sets the sample directly for all actions with the specified media type. + /// + /// The . + /// The sample. + /// The media type. + public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample); + } + + /// + /// Sets the sample directly for all actions with the specified type and media type. + /// + /// The . + /// The sample. + /// The media type. + /// The parameter type or return type of an action. + public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type) + { + config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample); + } + + /// + /// Specifies the actual type of passed to the in an action. + /// The help page will use this information to produce more accurate request samples. + /// + /// The . + /// The type. + /// Name of the controller. + /// Name of the action. + public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName) + { + config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type); + } + + /// + /// Specifies the actual type of passed to the in an action. + /// The help page will use this information to produce more accurate request samples. + /// + /// The . + /// The type. + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) + { + config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type); + } + + /// + /// Specifies the actual type of returned as part of the in an action. + /// The help page will use this information to produce more accurate response samples. + /// + /// The . + /// The type. + /// Name of the controller. + /// Name of the action. + public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName) + { + config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type); + } + + /// + /// Specifies the actual type of returned as part of the in an action. + /// The help page will use this information to produce more accurate response samples. + /// + /// The . + /// The type. + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) + { + config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type); + } + + /// + /// Gets the help page sample generator. + /// + /// The . + /// The help page sample generator. + public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config) + { + return (HelpPageSampleGenerator)config.Properties.GetOrAdd( + typeof(HelpPageSampleGenerator), + k => new HelpPageSampleGenerator()); + } + + /// + /// Sets the help page sample generator. + /// + /// The . + /// The help page sample generator. + public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator) + { + config.Properties.AddOrUpdate( + typeof(HelpPageSampleGenerator), + k => sampleGenerator, + (k, o) => sampleGenerator); + } + + /// + /// Gets the model description generator. + /// + /// The configuration. + /// The + public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config) + { + return (ModelDescriptionGenerator)config.Properties.GetOrAdd( + typeof(ModelDescriptionGenerator), + k => InitializeModelDescriptionGenerator(config)); + } + + /// + /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls. + /// + /// The . + /// The ID. + /// + /// An + /// + public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId) + { + object model; + string modelId = ApiModelPrefix + apiDescriptionId; + if (!config.Properties.TryGetValue(modelId, out model)) + { + Collection apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions; + ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase)); + if (apiDescription != null) + { + model = GenerateApiModel(apiDescription, config); + config.Properties.TryAdd(modelId, model); + } + } + + return (HelpPageApiModel)model; + } + + private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config) + { + HelpPageApiModel apiModel = new HelpPageApiModel() + { + ApiDescription = apiDescription, + }; + + ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator(); + HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); + GenerateUriParameters(apiModel, modelGenerator); + GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator); + GenerateResourceDescription(apiModel, modelGenerator); + GenerateSamples(apiModel, sampleGenerator); + + return apiModel; + } + + private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) + { + ApiDescription apiDescription = apiModel.ApiDescription; + foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) + { + if (apiParameter.Source == ApiParameterSource.FromUri) + { + HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor; + Type parameterType = null; + ModelDescription typeDescription = null; + ComplexTypeModelDescription complexTypeDescription = null; + if (parameterDescriptor != null) + { + parameterType = parameterDescriptor.ParameterType; + typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType); + complexTypeDescription = typeDescription as ComplexTypeModelDescription; + } + + // Example: + // [TypeConverter(typeof(PointConverter))] + // public class Point + // { + // public Point(int x, int y) + // { + // X = x; + // Y = y; + // } + // public int X { get; set; } + // public int Y { get; set; } + // } + // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection. + // + // public class Point + // { + // public int X { get; set; } + // public int Y { get; set; } + // } + // Regular complex class Point will have properties X and Y added to UriParameters collection. + if (complexTypeDescription != null + && !IsBindableWithTypeConverter(parameterType)) + { + foreach (ParameterDescription uriParameter in complexTypeDescription.Properties) + { + apiModel.UriParameters.Add(uriParameter); + } + } + else if (parameterDescriptor != null) + { + ParameterDescription uriParameter = + AddParameterDescription(apiModel, apiParameter, typeDescription); + + if (!parameterDescriptor.IsOptional) + { + uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" }); + } + + object defaultValue = parameterDescriptor.DefaultValue; + if (defaultValue != null) + { + uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }); + } + } + else + { + Debug.Assert(parameterDescriptor == null); + + // If parameterDescriptor is null, this is an undeclared route parameter which only occurs + // when source is FromUri. Ignored in request model and among resource parameters but listed + // as a simple string here. + ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string)); + AddParameterDescription(apiModel, apiParameter, modelDescription); + } + } + } + } + + private static bool IsBindableWithTypeConverter(Type parameterType) + { + if (parameterType == null) + { + return false; + } + + return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string)); + } + + private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel, + ApiParameterDescription apiParameter, ModelDescription typeDescription) + { + ParameterDescription parameterDescription = new ParameterDescription + { + Name = apiParameter.Name, + Documentation = apiParameter.Documentation, + TypeDescription = typeDescription, + }; + + apiModel.UriParameters.Add(parameterDescription); + return parameterDescription; + } + + private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator) + { + ApiDescription apiDescription = apiModel.ApiDescription; + foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) + { + if (apiParameter.Source == ApiParameterSource.FromBody) + { + Type parameterType = apiParameter.ParameterDescriptor.ParameterType; + apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); + apiModel.RequestDocumentation = apiParameter.Documentation; + } + else if (apiParameter.ParameterDescriptor != null && + apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)) + { + Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); + + if (parameterType != null) + { + apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); + } + } + } + } + + private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) + { + ResponseDescription response = apiModel.ApiDescription.ResponseDescription; + Type responseType = response.ResponseType ?? response.DeclaredType; + if (responseType != null && responseType != typeof(void)) + { + apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType); + } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")] + private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator) + { + try + { + foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription)) + { + apiModel.SampleRequests.Add(item.Key, item.Value); + LogInvalidSampleAsError(apiModel, item.Value); + } + + foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription)) + { + apiModel.SampleResponses.Add(item.Key, item.Value); + LogInvalidSampleAsError(apiModel, item.Value); + } + } + catch (Exception e) + { + apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture, + "An exception has occurred while generating the sample. Exception message: {0}", + HelpPageSampleGenerator.UnwrapException(e).Message)); + } + } + + private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType) + { + parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault( + p => p.Source == ApiParameterSource.FromBody || + (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))); + + if (parameterDescription == null) + { + resourceType = null; + return false; + } + + resourceType = parameterDescription.ParameterDescriptor.ParameterType; + + if (resourceType == typeof(HttpRequestMessage)) + { + HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); + resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); + } + + if (resourceType == null) + { + parameterDescription = null; + return false; + } + + return true; + } + + private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config) + { + ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config); + Collection apis = config.Services.GetApiExplorer().ApiDescriptions; + foreach (ApiDescription api in apis) + { + ApiParameterDescription parameterDescription; + Type parameterType; + if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType)) + { + modelGenerator.GetOrCreateModelDescription(parameterType); + } + } + return modelGenerator; + } + + private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample) + { + InvalidSample invalidSample = sample as InvalidSample; + if (invalidSample != null) + { + apiModel.ErrorMessages.Add(invalidSample.ErrorMessage); + } + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs new file mode 100644 index 000000000..4d98d93d7 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs @@ -0,0 +1,7 @@ +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class CollectionModelDescription : ModelDescription + { + public ModelDescription ElementDescription { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs new file mode 100644 index 000000000..f6bbdb791 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs @@ -0,0 +1,14 @@ +using System.Collections.ObjectModel; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class ComplexTypeModelDescription : ModelDescription + { + public ComplexTypeModelDescription() + { + Properties = new Collection(); + } + + public Collection Properties { get; private set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs new file mode 100644 index 000000000..49eb5f28a --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs @@ -0,0 +1,6 @@ +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class DictionaryModelDescription : KeyValuePairModelDescription + { + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs new file mode 100644 index 000000000..9222ab841 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class EnumTypeModelDescription : ModelDescription + { + public EnumTypeModelDescription() + { + Values = new Collection(); + } + + public Collection Values { get; private set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs new file mode 100644 index 000000000..7d835c87d --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs @@ -0,0 +1,11 @@ +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class EnumValueDescription + { + public string Documentation { get; set; } + + public string Name { get; set; } + + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs new file mode 100644 index 000000000..9201de2d7 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs @@ -0,0 +1,12 @@ +using System; +using System.Reflection; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public interface IModelDocumentationProvider + { + string GetDocumentation(MemberInfo member); + + string GetDocumentation(Type type); + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs new file mode 100644 index 000000000..8359be9ac --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs @@ -0,0 +1,9 @@ +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class KeyValuePairModelDescription : ModelDescription + { + public ModelDescription KeyModelDescription { get; set; } + + public ModelDescription ValueModelDescription { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescription.cs new file mode 100644 index 000000000..a1a82df97 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescription.cs @@ -0,0 +1,16 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + /// + /// Describes a type model. + /// + public abstract class ModelDescription + { + public string Documentation { get; set; } + + public Type ModelType { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs new file mode 100644 index 000000000..535921adb --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Web.Http; +using System.Web.Http.Description; +using System.Xml.Serialization; +using Newtonsoft.Json; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + /// + /// Generates model descriptions for given types. + /// + public class ModelDescriptionGenerator + { + // Modify this to support more data annotation attributes. + private readonly IDictionary> AnnotationTextGenerator = new Dictionary> + { + { typeof(RequiredAttribute), a => "Required" }, + { typeof(RangeAttribute), a => + { + RangeAttribute range = (RangeAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum); + } + }, + { typeof(MaxLengthAttribute), a => + { + MaxLengthAttribute maxLength = (MaxLengthAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length); + } + }, + { typeof(MinLengthAttribute), a => + { + MinLengthAttribute minLength = (MinLengthAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length); + } + }, + { typeof(StringLengthAttribute), a => + { + StringLengthAttribute strLength = (StringLengthAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength); + } + }, + { typeof(DataTypeAttribute), a => + { + DataTypeAttribute dataType = (DataTypeAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString()); + } + }, + { typeof(RegularExpressionAttribute), a => + { + RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a; + return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern); + } + }, + }; + + // Modify this to add more default documentations. + private readonly IDictionary DefaultTypeDocumentation = new Dictionary + { + { typeof(Int16), "integer" }, + { typeof(Int32), "integer" }, + { typeof(Int64), "integer" }, + { typeof(UInt16), "unsigned integer" }, + { typeof(UInt32), "unsigned integer" }, + { typeof(UInt64), "unsigned integer" }, + { typeof(Byte), "byte" }, + { typeof(Char), "character" }, + { typeof(SByte), "signed byte" }, + { typeof(Uri), "URI" }, + { typeof(Single), "decimal number" }, + { typeof(Double), "decimal number" }, + { typeof(Decimal), "decimal number" }, + { typeof(String), "string" }, + { typeof(Guid), "globally unique identifier" }, + { typeof(TimeSpan), "time interval" }, + { typeof(DateTime), "date" }, + { typeof(DateTimeOffset), "date" }, + { typeof(Boolean), "boolean" }, + }; + + private Lazy _documentationProvider; + + public ModelDescriptionGenerator(HttpConfiguration config) + { + if (config == null) + { + throw new ArgumentNullException("config"); + } + + _documentationProvider = new Lazy(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider); + GeneratedModels = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public Dictionary GeneratedModels { get; private set; } + + private IModelDocumentationProvider DocumentationProvider + { + get + { + return _documentationProvider.Value; + } + } + + public ModelDescription GetOrCreateModelDescription(Type modelType) + { + if (modelType == null) + { + throw new ArgumentNullException("modelType"); + } + + Type underlyingType = Nullable.GetUnderlyingType(modelType); + if (underlyingType != null) + { + modelType = underlyingType; + } + + ModelDescription modelDescription; + string modelName = ModelNameHelper.GetModelName(modelType); + if (GeneratedModels.TryGetValue(modelName, out modelDescription)) + { + if (modelType != modelDescription.ModelType) + { + throw new InvalidOperationException( + String.Format( + CultureInfo.CurrentCulture, + "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " + + "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.", + modelName, + modelDescription.ModelType.FullName, + modelType.FullName)); + } + + return modelDescription; + } + + if (DefaultTypeDocumentation.ContainsKey(modelType)) + { + return GenerateSimpleTypeModelDescription(modelType); + } + + if (modelType.IsEnum) + { + return GenerateEnumTypeModelDescription(modelType); + } + + if (modelType.IsGenericType) + { + Type[] genericArguments = modelType.GetGenericArguments(); + + if (genericArguments.Length == 1) + { + Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments); + if (enumerableType.IsAssignableFrom(modelType)) + { + return GenerateCollectionModelDescription(modelType, genericArguments[0]); + } + } + if (genericArguments.Length == 2) + { + Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments); + if (dictionaryType.IsAssignableFrom(modelType)) + { + return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]); + } + + Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments); + if (keyValuePairType.IsAssignableFrom(modelType)) + { + return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]); + } + } + } + + if (modelType.IsArray) + { + Type elementType = modelType.GetElementType(); + return GenerateCollectionModelDescription(modelType, elementType); + } + + if (modelType == typeof(NameValueCollection)) + { + return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string)); + } + + if (typeof(IDictionary).IsAssignableFrom(modelType)) + { + return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object)); + } + + if (typeof(IEnumerable).IsAssignableFrom(modelType)) + { + return GenerateCollectionModelDescription(modelType, typeof(object)); + } + + return GenerateComplexTypeModelDescription(modelType); + } + + // Change this to provide different name for the member. + private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute) + { + JsonPropertyAttribute jsonProperty = member.GetCustomAttribute(); + if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName)) + { + return jsonProperty.PropertyName; + } + + if (hasDataContractAttribute) + { + DataMemberAttribute dataMember = member.GetCustomAttribute(); + if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name)) + { + return dataMember.Name; + } + } + + return member.Name; + } + + private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute) + { + JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute(); + XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute(); + IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute(); + NonSerializedAttribute nonSerialized = member.GetCustomAttribute(); + ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute(); + + bool hasMemberAttribute = member.DeclaringType.IsEnum ? + member.GetCustomAttribute() != null : + member.GetCustomAttribute() != null; + + // Display member only if all the followings are true: + // no JsonIgnoreAttribute + // no XmlIgnoreAttribute + // no IgnoreDataMemberAttribute + // no NonSerializedAttribute + // no ApiExplorerSettingsAttribute with IgnoreApi set to true + // no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute + return jsonIgnore == null && + xmlIgnore == null && + ignoreDataMember == null && + nonSerialized == null && + (apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) && + (!hasDataContractAttribute || hasMemberAttribute); + } + + private string CreateDefaultDocumentation(Type type) + { + string documentation; + if (DefaultTypeDocumentation.TryGetValue(type, out documentation)) + { + return documentation; + } + if (DocumentationProvider != null) + { + documentation = DocumentationProvider.GetDocumentation(type); + } + + return documentation; + } + + private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel) + { + List annotations = new List(); + + IEnumerable attributes = property.GetCustomAttributes(); + foreach (Attribute attribute in attributes) + { + Func textGenerator; + if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator)) + { + annotations.Add( + new ParameterAnnotation + { + AnnotationAttribute = attribute, + Documentation = textGenerator(attribute) + }); + } + } + + // Rearrange the annotations + annotations.Sort((x, y) => + { + // Special-case RequiredAttribute so that it shows up on top + if (x.AnnotationAttribute is RequiredAttribute) + { + return -1; + } + if (y.AnnotationAttribute is RequiredAttribute) + { + return 1; + } + + // Sort the rest based on alphabetic order of the documentation + return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase); + }); + + foreach (ParameterAnnotation annotation in annotations) + { + propertyModel.Annotations.Add(annotation); + } + } + + private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType) + { + ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType); + if (collectionModelDescription != null) + { + return new CollectionModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + ElementDescription = collectionModelDescription + }; + } + + return null; + } + + private ModelDescription GenerateComplexTypeModelDescription(Type modelType) + { + ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + Documentation = CreateDefaultDocumentation(modelType) + }; + + GeneratedModels.Add(complexModelDescription.Name, complexModelDescription); + bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; + PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo property in properties) + { + if (ShouldDisplayMember(property, hasDataContractAttribute)) + { + ParameterDescription propertyModel = new ParameterDescription + { + Name = GetMemberName(property, hasDataContractAttribute) + }; + + if (DocumentationProvider != null) + { + propertyModel.Documentation = DocumentationProvider.GetDocumentation(property); + } + + GenerateAnnotations(property, propertyModel); + complexModelDescription.Properties.Add(propertyModel); + propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType); + } + } + + FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance); + foreach (FieldInfo field in fields) + { + if (ShouldDisplayMember(field, hasDataContractAttribute)) + { + ParameterDescription propertyModel = new ParameterDescription + { + Name = GetMemberName(field, hasDataContractAttribute) + }; + + if (DocumentationProvider != null) + { + propertyModel.Documentation = DocumentationProvider.GetDocumentation(field); + } + + complexModelDescription.Properties.Add(propertyModel); + propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType); + } + } + + return complexModelDescription; + } + + private DictionaryModelDescription GenerateDictionaryModelDescription(Type modelType, Type keyType, Type valueType) + { + ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); + ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); + + return new DictionaryModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + KeyModelDescription = keyModelDescription, + ValueModelDescription = valueModelDescription + }; + } + + private EnumTypeModelDescription GenerateEnumTypeModelDescription(Type modelType) + { + EnumTypeModelDescription enumDescription = new EnumTypeModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + Documentation = CreateDefaultDocumentation(modelType) + }; + bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; + foreach (FieldInfo field in modelType.GetFields(BindingFlags.Public | BindingFlags.Static)) + { + if (ShouldDisplayMember(field, hasDataContractAttribute)) + { + EnumValueDescription enumValue = new EnumValueDescription + { + Name = field.Name, + Value = field.GetRawConstantValue().ToString() + }; + if (DocumentationProvider != null) + { + enumValue.Documentation = DocumentationProvider.GetDocumentation(field); + } + enumDescription.Values.Add(enumValue); + } + } + GeneratedModels.Add(enumDescription.Name, enumDescription); + + return enumDescription; + } + + private KeyValuePairModelDescription GenerateKeyValuePairModelDescription(Type modelType, Type keyType, Type valueType) + { + ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); + ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); + + return new KeyValuePairModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + KeyModelDescription = keyModelDescription, + ValueModelDescription = valueModelDescription + }; + } + + private ModelDescription GenerateSimpleTypeModelDescription(Type modelType) + { + SimpleTypeModelDescription simpleModelDescription = new SimpleTypeModelDescription + { + Name = ModelNameHelper.GetModelName(modelType), + ModelType = modelType, + Documentation = CreateDefaultDocumentation(modelType) + }; + GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription); + + return simpleModelDescription; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs new file mode 100644 index 000000000..48bb108d5 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + /// + /// Use this attribute to change the name of the generated for a type. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)] + public sealed class ModelNameAttribute : Attribute + { + public ModelNameAttribute(string name) + { + Name = name; + } + + public string Name { get; private set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs new file mode 100644 index 000000000..48d7e53e5 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs @@ -0,0 +1,36 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + internal static class ModelNameHelper + { + // Modify this to provide custom model name mapping. + public static string GetModelName(Type type) + { + ModelNameAttribute modelNameAttribute = type.GetCustomAttribute(); + if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name)) + { + return modelNameAttribute.Name; + } + + string modelName = type.Name; + if (type.IsGenericType) + { + // Format the generic type name to something like: GenericOfAgurment1AndArgument2 + Type genericType = type.GetGenericTypeDefinition(); + Type[] genericArguments = type.GetGenericArguments(); + string genericTypeName = genericType.Name; + + // Trim the generic parameter counts from the name + genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); + string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray(); + modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames)); + } + + return modelName; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs new file mode 100644 index 000000000..12f9daacb --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs @@ -0,0 +1,11 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class ParameterAnnotation + { + public Attribute AnnotationAttribute { get; set; } + + public string Documentation { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs new file mode 100644 index 000000000..6902131a2 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class ParameterDescription + { + public ParameterDescription() + { + Annotations = new Collection(); + } + + public Collection Annotations { get; private set; } + + public string Documentation { get; set; } + + public string Name { get; set; } + + public ModelDescription TypeDescription { get; set; } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs new file mode 100644 index 000000000..e1178e2ed --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs @@ -0,0 +1,6 @@ +namespace OwinWebApi.Areas.HelpPage.ModelDescriptions +{ + public class SimpleTypeModelDescription : ModelDescription + { + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Models/HelpPageApiModel.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Models/HelpPageApiModel.cs new file mode 100644 index 000000000..568a809b1 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Models/HelpPageApiModel.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Net.Http.Headers; +using System.Web.Http.Description; +using OwinWebApi.Areas.HelpPage.ModelDescriptions; + +namespace OwinWebApi.Areas.HelpPage.Models +{ + /// + /// The model that represents an API displayed on the help page. + /// + public class HelpPageApiModel + { + /// + /// Initializes a new instance of the class. + /// + public HelpPageApiModel() + { + UriParameters = new Collection(); + SampleRequests = new Dictionary(); + SampleResponses = new Dictionary(); + ErrorMessages = new Collection(); + } + + /// + /// Gets or sets the that describes the API. + /// + public ApiDescription ApiDescription { get; set; } + + /// + /// Gets or sets the collection that describes the URI parameters for the API. + /// + public Collection UriParameters { get; private set; } + + /// + /// Gets or sets the documentation for the request. + /// + public string RequestDocumentation { get; set; } + + /// + /// Gets or sets the that describes the request body. + /// + public ModelDescription RequestModelDescription { get; set; } + + /// + /// Gets the request body parameter descriptions. + /// + public IList RequestBodyParameters + { + get + { + return GetParameterDescriptions(RequestModelDescription); + } + } + + /// + /// Gets or sets the that describes the resource. + /// + public ModelDescription ResourceDescription { get; set; } + + /// + /// Gets the resource property descriptions. + /// + public IList ResourceProperties + { + get + { + return GetParameterDescriptions(ResourceDescription); + } + } + + /// + /// Gets the sample requests associated with the API. + /// + public IDictionary SampleRequests { get; private set; } + + /// + /// Gets the sample responses associated with the API. + /// + public IDictionary SampleResponses { get; private set; } + + /// + /// Gets the error messages associated with this model. + /// + public Collection ErrorMessages { get; private set; } + + private static IList GetParameterDescriptions(ModelDescription modelDescription) + { + ComplexTypeModelDescription complexTypeModelDescription = modelDescription as ComplexTypeModelDescription; + if (complexTypeModelDescription != null) + { + return complexTypeModelDescription.Properties; + } + + CollectionModelDescription collectionModelDescription = modelDescription as CollectionModelDescription; + if (collectionModelDescription != null) + { + complexTypeModelDescription = collectionModelDescription.ElementDescription as ComplexTypeModelDescription; + if (complexTypeModelDescription != null) + { + return complexTypeModelDescription.Properties; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs new file mode 100644 index 000000000..6c798b612 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs @@ -0,0 +1,444 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Web.Http.Description; +using System.Xml.Linq; +using Newtonsoft.Json; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This class will generate the samples for the help page. + /// + public class HelpPageSampleGenerator + { + /// + /// Initializes a new instance of the class. + /// + public HelpPageSampleGenerator() + { + ActualHttpMessageTypes = new Dictionary(); + ActionSamples = new Dictionary(); + SampleObjects = new Dictionary(); + SampleObjectFactories = new List> + { + DefaultSampleObjectFactory, + }; + } + + /// + /// Gets CLR types that are used as the content of or . + /// + public IDictionary ActualHttpMessageTypes { get; internal set; } + + /// + /// Gets the objects that are used directly as samples for certain actions. + /// + public IDictionary ActionSamples { get; internal set; } + + /// + /// Gets the objects that are serialized as samples by the supported formatters. + /// + public IDictionary SampleObjects { get; internal set; } + + /// + /// Gets factories for the objects that the supported formatters will serialize as samples. Processed in order, + /// stopping when the factory successfully returns a non- object. + /// + /// + /// Collection includes just initially. Use + /// SampleObjectFactories.Insert(0, func) to provide an override and + /// SampleObjectFactories.Add(func) to provide a fallback. + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", + Justification = "This is an appropriate nesting of generic types")] + public IList> SampleObjectFactories { get; private set; } + + /// + /// Gets the request body samples for a given . + /// + /// The . + /// The samples keyed by media type. + public IDictionary GetSampleRequests(ApiDescription api) + { + return GetSample(api, SampleDirection.Request); + } + + /// + /// Gets the response body samples for a given . + /// + /// The . + /// The samples keyed by media type. + public IDictionary GetSampleResponses(ApiDescription api) + { + return GetSample(api, SampleDirection.Response); + } + + /// + /// Gets the request or response body samples. + /// + /// The . + /// The value indicating whether the sample is for a request or for a response. + /// The samples keyed by media type. + public virtual IDictionary GetSample(ApiDescription api, SampleDirection sampleDirection) + { + if (api == null) + { + throw new ArgumentNullException("api"); + } + string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; + string actionName = api.ActionDescriptor.ActionName; + IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); + Collection formatters; + Type type = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, out formatters); + var samples = new Dictionary(); + + // Use the samples provided directly for actions + var actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection); + foreach (var actionSample in actionSamples) + { + samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value)); + } + + // Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage. + // Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters. + if (type != null && !typeof(HttpResponseMessage).IsAssignableFrom(type)) + { + object sampleObject = GetSampleObject(type); + foreach (var formatter in formatters) + { + foreach (MediaTypeHeaderValue mediaType in formatter.SupportedMediaTypes) + { + if (!samples.ContainsKey(mediaType)) + { + object sample = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection); + + // If no sample found, try generate sample using formatter and sample object + if (sample == null && sampleObject != null) + { + sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType); + } + + samples.Add(mediaType, WrapSampleIfString(sample)); + } + } + } + } + + return samples; + } + + /// + /// Search for samples that are provided directly through . + /// + /// Name of the controller. + /// Name of the action. + /// The parameter names. + /// The CLR type. + /// The formatter. + /// The media type. + /// The value indicating whether the sample is for a request or for a response. + /// The sample that matches the parameters. + public virtual object GetActionSample(string controllerName, string actionName, IEnumerable parameterNames, Type type, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, SampleDirection sampleDirection) + { + object sample; + + // First, try to get the sample provided for the specified mediaType, sampleDirection, controllerName, actionName and parameterNames. + // If not found, try to get the sample provided for the specified mediaType, sampleDirection, controllerName and actionName regardless of the parameterNames. + // If still not found, try to get the sample provided for the specified mediaType and type. + // Finally, try to get the sample provided for the specified mediaType. + if (ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, parameterNames), out sample) || + ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, new[] { "*" }), out sample) || + ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, type), out sample) || + ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType), out sample)) + { + return sample; + } + + return null; + } + + /// + /// Gets the sample object that will be serialized by the formatters. + /// First, it will look at the . If no sample object is found, it will try to create + /// one using (which wraps an ) and other + /// factories in . + /// + /// The type. + /// The sample object. + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Even if all items in SampleObjectFactories throw, problem will be visible as missing sample.")] + public virtual object GetSampleObject(Type type) + { + object sampleObject; + + if (!SampleObjects.TryGetValue(type, out sampleObject)) + { + // No specific object available, try our factories. + foreach (Func factory in SampleObjectFactories) + { + if (factory == null) + { + continue; + } + + try + { + sampleObject = factory(this, type); + if (sampleObject != null) + { + break; + } + } + catch + { + // Ignore any problems encountered in the factory; go on to the next one (if any). + } + } + } + + return sampleObject; + } + + /// + /// Resolves the actual type of passed to the in an action. + /// + /// The . + /// The type. + public virtual Type ResolveHttpRequestMessageType(ApiDescription api) + { + string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; + string actionName = api.ActionDescriptor.ActionName; + IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); + Collection formatters; + return ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, out formatters); + } + + /// + /// Resolves the type of the action parameter or return value when or is used. + /// + /// The . + /// Name of the controller. + /// Name of the action. + /// The parameter names. + /// The value indicating whether the sample is for a request or a response. + /// The formatters. + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is only used in advanced scenarios.")] + public virtual Type ResolveType(ApiDescription api, string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection, out Collection formatters) + { + if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) + { + throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); + } + if (api == null) + { + throw new ArgumentNullException("api"); + } + Type type; + if (ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), out type) || + ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, new[] { "*" }), out type)) + { + // Re-compute the supported formatters based on type + Collection newFormatters = new Collection(); + foreach (var formatter in api.ActionDescriptor.Configuration.Formatters) + { + if (IsFormatSupported(sampleDirection, formatter, type)) + { + newFormatters.Add(formatter); + } + } + formatters = newFormatters; + } + else + { + switch (sampleDirection) + { + case SampleDirection.Request: + ApiParameterDescription requestBodyParameter = api.ParameterDescriptions.FirstOrDefault(p => p.Source == ApiParameterSource.FromBody); + type = requestBodyParameter == null ? null : requestBodyParameter.ParameterDescriptor.ParameterType; + formatters = api.SupportedRequestBodyFormatters; + break; + case SampleDirection.Response: + default: + type = api.ResponseDescription.ResponseType ?? api.ResponseDescription.DeclaredType; + formatters = api.SupportedResponseFormatters; + break; + } + } + + return type; + } + + /// + /// Writes the sample object using formatter. + /// + /// The formatter. + /// The value. + /// The type. + /// Type of the media. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as InvalidSample.")] + public virtual object WriteSampleObjectUsingFormatter(MediaTypeFormatter formatter, object value, Type type, MediaTypeHeaderValue mediaType) + { + if (formatter == null) + { + throw new ArgumentNullException("formatter"); + } + if (mediaType == null) + { + throw new ArgumentNullException("mediaType"); + } + + object sample = String.Empty; + MemoryStream ms = null; + HttpContent content = null; + try + { + if (formatter.CanWriteType(type)) + { + ms = new MemoryStream(); + content = new ObjectContent(type, value, formatter, mediaType); + formatter.WriteToStreamAsync(type, value, ms, content, null).Wait(); + ms.Position = 0; + StreamReader reader = new StreamReader(ms); + string serializedSampleString = reader.ReadToEnd(); + if (mediaType.MediaType.ToUpperInvariant().Contains("XML")) + { + serializedSampleString = TryFormatXml(serializedSampleString); + } + else if (mediaType.MediaType.ToUpperInvariant().Contains("JSON")) + { + serializedSampleString = TryFormatJson(serializedSampleString); + } + + sample = new TextSample(serializedSampleString); + } + else + { + sample = new InvalidSample(String.Format( + CultureInfo.CurrentCulture, + "Failed to generate the sample for media type '{0}'. Cannot use formatter '{1}' to write type '{2}'.", + mediaType, + formatter.GetType().Name, + type.Name)); + } + } + catch (Exception e) + { + sample = new InvalidSample(String.Format( + CultureInfo.CurrentCulture, + "An exception has occurred while using the formatter '{0}' to generate sample for media type '{1}'. Exception message: {2}", + formatter.GetType().Name, + mediaType.MediaType, + UnwrapException(e).Message)); + } + finally + { + if (ms != null) + { + ms.Dispose(); + } + if (content != null) + { + content.Dispose(); + } + } + + return sample; + } + + internal static Exception UnwrapException(Exception exception) + { + AggregateException aggregateException = exception as AggregateException; + if (aggregateException != null) + { + return aggregateException.Flatten().InnerException; + } + return exception; + } + + // Default factory for sample objects + private static object DefaultSampleObjectFactory(HelpPageSampleGenerator sampleGenerator, Type type) + { + // Try to create a default sample object + ObjectGenerator objectGenerator = new ObjectGenerator(); + return objectGenerator.GenerateObject(type); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] + private static string TryFormatJson(string str) + { + try + { + object parsedJson = JsonConvert.DeserializeObject(str); + return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); + } + catch + { + // can't parse JSON, return the original string + return str; + } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] + private static string TryFormatXml(string str) + { + try + { + XDocument xml = XDocument.Parse(str); + return xml.ToString(); + } + catch + { + // can't parse XML, return the original string + return str; + } + } + + private static bool IsFormatSupported(SampleDirection sampleDirection, MediaTypeFormatter formatter, Type type) + { + switch (sampleDirection) + { + case SampleDirection.Request: + return formatter.CanReadType(type); + case SampleDirection.Response: + return formatter.CanWriteType(type); + } + return false; + } + + private IEnumerable> GetAllActionSamples(string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection) + { + HashSet parameterNamesSet = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); + foreach (var sample in ActionSamples) + { + HelpPageSampleKey sampleKey = sample.Key; + if (String.Equals(controllerName, sampleKey.ControllerName, StringComparison.OrdinalIgnoreCase) && + String.Equals(actionName, sampleKey.ActionName, StringComparison.OrdinalIgnoreCase) && + (sampleKey.ParameterNames.SetEquals(new[] { "*" }) || parameterNamesSet.SetEquals(sampleKey.ParameterNames)) && + sampleDirection == sampleKey.SampleDirection) + { + yield return sample; + } + } + } + + private static object WrapSampleIfString(object sample) + { + string stringSample = sample as string; + if (stringSample != null) + { + return new TextSample(stringSample); + } + + return sample; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs new file mode 100644 index 000000000..015ca1bd4 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Net.Http.Headers; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This is used to identify the place where the sample should be applied. + /// + public class HelpPageSampleKey + { + /// + /// Creates a new based on media type. + /// + /// The media type. + public HelpPageSampleKey(MediaTypeHeaderValue mediaType) + { + if (mediaType == null) + { + throw new ArgumentNullException("mediaType"); + } + + ActionName = String.Empty; + ControllerName = String.Empty; + MediaType = mediaType; + ParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + /// + /// Creates a new based on media type and CLR type. + /// + /// The media type. + /// The CLR type. + public HelpPageSampleKey(MediaTypeHeaderValue mediaType, Type type) + : this(mediaType) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + ParameterType = type; + } + + /// + /// Creates a new based on , controller name, action name and parameter names. + /// + /// The . + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public HelpPageSampleKey(SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) + { + if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) + { + throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); + } + if (controllerName == null) + { + throw new ArgumentNullException("controllerName"); + } + if (actionName == null) + { + throw new ArgumentNullException("actionName"); + } + if (parameterNames == null) + { + throw new ArgumentNullException("parameterNames"); + } + + ControllerName = controllerName; + ActionName = actionName; + ParameterNames = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); + SampleDirection = sampleDirection; + } + + /// + /// Creates a new based on media type, , controller name, action name and parameter names. + /// + /// The media type. + /// The . + /// Name of the controller. + /// Name of the action. + /// The parameter names. + public HelpPageSampleKey(MediaTypeHeaderValue mediaType, SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) + : this(sampleDirection, controllerName, actionName, parameterNames) + { + if (mediaType == null) + { + throw new ArgumentNullException("mediaType"); + } + + MediaType = mediaType; + } + + /// + /// Gets the name of the controller. + /// + /// + /// The name of the controller. + /// + public string ControllerName { get; private set; } + + /// + /// Gets the name of the action. + /// + /// + /// The name of the action. + /// + public string ActionName { get; private set; } + + /// + /// Gets the media type. + /// + /// + /// The media type. + /// + public MediaTypeHeaderValue MediaType { get; private set; } + + /// + /// Gets the parameter names. + /// + public HashSet ParameterNames { get; private set; } + + public Type ParameterType { get; private set; } + + /// + /// Gets the . + /// + public SampleDirection? SampleDirection { get; private set; } + + public override bool Equals(object obj) + { + HelpPageSampleKey otherKey = obj as HelpPageSampleKey; + if (otherKey == null) + { + return false; + } + + return String.Equals(ControllerName, otherKey.ControllerName, StringComparison.OrdinalIgnoreCase) && + String.Equals(ActionName, otherKey.ActionName, StringComparison.OrdinalIgnoreCase) && + (MediaType == otherKey.MediaType || (MediaType != null && MediaType.Equals(otherKey.MediaType))) && + ParameterType == otherKey.ParameterType && + SampleDirection == otherKey.SampleDirection && + ParameterNames.SetEquals(otherKey.ParameterNames); + } + + public override int GetHashCode() + { + int hashCode = ControllerName.ToUpperInvariant().GetHashCode() ^ ActionName.ToUpperInvariant().GetHashCode(); + if (MediaType != null) + { + hashCode ^= MediaType.GetHashCode(); + } + if (SampleDirection != null) + { + hashCode ^= SampleDirection.GetHashCode(); + } + if (ParameterType != null) + { + hashCode ^= ParameterType.GetHashCode(); + } + foreach (string parameterName in ParameterNames) + { + hashCode ^= parameterName.ToUpperInvariant().GetHashCode(); + } + + return hashCode; + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ImageSample.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ImageSample.cs new file mode 100644 index 000000000..06a40c8b4 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ImageSample.cs @@ -0,0 +1,41 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This represents an image sample on the help page. There's a display template named ImageSample associated with this class. + /// + public class ImageSample + { + /// + /// Initializes a new instance of the class. + /// + /// The URL of an image. + public ImageSample(string src) + { + if (src == null) + { + throw new ArgumentNullException("src"); + } + Src = src; + } + + public string Src { get; private set; } + + public override bool Equals(object obj) + { + ImageSample other = obj as ImageSample; + return other != null && Src == other.Src; + } + + public override int GetHashCode() + { + return Src.GetHashCode(); + } + + public override string ToString() + { + return Src; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/InvalidSample.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/InvalidSample.cs new file mode 100644 index 000000000..9824b832c --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/InvalidSample.cs @@ -0,0 +1,37 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. + /// + public class InvalidSample + { + public InvalidSample(string errorMessage) + { + if (errorMessage == null) + { + throw new ArgumentNullException("errorMessage"); + } + ErrorMessage = errorMessage; + } + + public string ErrorMessage { get; private set; } + + public override bool Equals(object obj) + { + InvalidSample other = obj as InvalidSample; + return other != null && ErrorMessage == other.ErrorMessage; + } + + public override int GetHashCode() + { + return ErrorMessage.GetHashCode(); + } + + public override string ToString() + { + return ErrorMessage; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs new file mode 100644 index 000000000..15ed4c8f6 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs @@ -0,0 +1,456 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This class will create an object of a given type and populate it with sample data. + /// + public class ObjectGenerator + { + internal const int DefaultCollectionSize = 2; + private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator(); + + /// + /// Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types: + /// Simple types: , , , , , etc. + /// Complex types: POCO types. + /// Nullables: . + /// Arrays: arrays of simple types or complex types. + /// Key value pairs: + /// Tuples: , , etc + /// Dictionaries: or anything deriving from . + /// Collections: , , , , , or anything deriving from or . + /// Queryables: , . + /// + /// The type. + /// An object of the given type. + public object GenerateObject(Type type) + { + return GenerateObject(type, new Dictionary()); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")] + private object GenerateObject(Type type, Dictionary createdObjectReferences) + { + try + { + if (SimpleTypeObjectGenerator.CanGenerateObject(type)) + { + return SimpleObjectGenerator.GenerateObject(type); + } + + if (type.IsArray) + { + return GenerateArray(type, DefaultCollectionSize, createdObjectReferences); + } + + if (type.IsGenericType) + { + return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences); + } + + if (type == typeof(IDictionary)) + { + return GenerateDictionary(typeof(Hashtable), DefaultCollectionSize, createdObjectReferences); + } + + if (typeof(IDictionary).IsAssignableFrom(type)) + { + return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences); + } + + if (type == typeof(IList) || + type == typeof(IEnumerable) || + type == typeof(ICollection)) + { + return GenerateCollection(typeof(ArrayList), DefaultCollectionSize, createdObjectReferences); + } + + if (typeof(IList).IsAssignableFrom(type)) + { + return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences); + } + + if (type == typeof(IQueryable)) + { + return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences); + } + + if (type.IsEnum) + { + return GenerateEnum(type); + } + + if (type.IsPublic || type.IsNestedPublic) + { + return GenerateComplexObject(type, createdObjectReferences); + } + } + catch + { + // Returns null if anything fails + return null; + } + + return null; + } + + private static object GenerateGenericType(Type type, int collectionSize, Dictionary createdObjectReferences) + { + Type genericTypeDefinition = type.GetGenericTypeDefinition(); + if (genericTypeDefinition == typeof(Nullable<>)) + { + return GenerateNullable(type, createdObjectReferences); + } + + if (genericTypeDefinition == typeof(KeyValuePair<,>)) + { + return GenerateKeyValuePair(type, createdObjectReferences); + } + + if (IsTuple(genericTypeDefinition)) + { + return GenerateTuple(type, createdObjectReferences); + } + + Type[] genericArguments = type.GetGenericArguments(); + if (genericArguments.Length == 1) + { + if (genericTypeDefinition == typeof(IList<>) || + genericTypeDefinition == typeof(IEnumerable<>) || + genericTypeDefinition == typeof(ICollection<>)) + { + Type collectionType = typeof(List<>).MakeGenericType(genericArguments); + return GenerateCollection(collectionType, collectionSize, createdObjectReferences); + } + + if (genericTypeDefinition == typeof(IQueryable<>)) + { + return GenerateQueryable(type, collectionSize, createdObjectReferences); + } + + Type closedCollectionType = typeof(ICollection<>).MakeGenericType(genericArguments[0]); + if (closedCollectionType.IsAssignableFrom(type)) + { + return GenerateCollection(type, collectionSize, createdObjectReferences); + } + } + + if (genericArguments.Length == 2) + { + if (genericTypeDefinition == typeof(IDictionary<,>)) + { + Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(genericArguments); + return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences); + } + + Type closedDictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments[0], genericArguments[1]); + if (closedDictionaryType.IsAssignableFrom(type)) + { + return GenerateDictionary(type, collectionSize, createdObjectReferences); + } + } + + if (type.IsPublic || type.IsNestedPublic) + { + return GenerateComplexObject(type, createdObjectReferences); + } + + return null; + } + + private static object GenerateTuple(Type type, Dictionary createdObjectReferences) + { + Type[] genericArgs = type.GetGenericArguments(); + object[] parameterValues = new object[genericArgs.Length]; + bool failedToCreateTuple = true; + ObjectGenerator objectGenerator = new ObjectGenerator(); + for (int i = 0; i < genericArgs.Length; i++) + { + parameterValues[i] = objectGenerator.GenerateObject(genericArgs[i], createdObjectReferences); + failedToCreateTuple &= parameterValues[i] == null; + } + if (failedToCreateTuple) + { + return null; + } + object result = Activator.CreateInstance(type, parameterValues); + return result; + } + + private static bool IsTuple(Type genericTypeDefinition) + { + return genericTypeDefinition == typeof(Tuple<>) || + genericTypeDefinition == typeof(Tuple<,>) || + genericTypeDefinition == typeof(Tuple<,,>) || + genericTypeDefinition == typeof(Tuple<,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,,,>); + } + + private static object GenerateKeyValuePair(Type keyValuePairType, Dictionary createdObjectReferences) + { + Type[] genericArgs = keyValuePairType.GetGenericArguments(); + Type typeK = genericArgs[0]; + Type typeV = genericArgs[1]; + ObjectGenerator objectGenerator = new ObjectGenerator(); + object keyObject = objectGenerator.GenerateObject(typeK, createdObjectReferences); + object valueObject = objectGenerator.GenerateObject(typeV, createdObjectReferences); + if (keyObject == null && valueObject == null) + { + // Failed to create key and values + return null; + } + object result = Activator.CreateInstance(keyValuePairType, keyObject, valueObject); + return result; + } + + private static object GenerateArray(Type arrayType, int size, Dictionary createdObjectReferences) + { + Type type = arrayType.GetElementType(); + Array result = Array.CreateInstance(type, size); + bool areAllElementsNull = true; + ObjectGenerator objectGenerator = new ObjectGenerator(); + for (int i = 0; i < size; i++) + { + object element = objectGenerator.GenerateObject(type, createdObjectReferences); + result.SetValue(element, i); + areAllElementsNull &= element == null; + } + + if (areAllElementsNull) + { + return null; + } + + return result; + } + + private static object GenerateDictionary(Type dictionaryType, int size, Dictionary createdObjectReferences) + { + Type typeK = typeof(object); + Type typeV = typeof(object); + if (dictionaryType.IsGenericType) + { + Type[] genericArgs = dictionaryType.GetGenericArguments(); + typeK = genericArgs[0]; + typeV = genericArgs[1]; + } + + object result = Activator.CreateInstance(dictionaryType); + MethodInfo addMethod = dictionaryType.GetMethod("Add") ?? dictionaryType.GetMethod("TryAdd"); + MethodInfo containsMethod = dictionaryType.GetMethod("Contains") ?? dictionaryType.GetMethod("ContainsKey"); + ObjectGenerator objectGenerator = new ObjectGenerator(); + for (int i = 0; i < size; i++) + { + object newKey = objectGenerator.GenerateObject(typeK, createdObjectReferences); + if (newKey == null) + { + // Cannot generate a valid key + return null; + } + + bool containsKey = (bool)containsMethod.Invoke(result, new object[] { newKey }); + if (!containsKey) + { + object newValue = objectGenerator.GenerateObject(typeV, createdObjectReferences); + addMethod.Invoke(result, new object[] { newKey, newValue }); + } + } + + return result; + } + + private static object GenerateEnum(Type enumType) + { + Array possibleValues = Enum.GetValues(enumType); + if (possibleValues.Length > 0) + { + return possibleValues.GetValue(0); + } + return null; + } + + private static object GenerateQueryable(Type queryableType, int size, Dictionary createdObjectReferences) + { + bool isGeneric = queryableType.IsGenericType; + object list; + if (isGeneric) + { + Type listType = typeof(List<>).MakeGenericType(queryableType.GetGenericArguments()); + list = GenerateCollection(listType, size, createdObjectReferences); + } + else + { + list = GenerateArray(typeof(object[]), size, createdObjectReferences); + } + if (list == null) + { + return null; + } + if (isGeneric) + { + Type argumentType = typeof(IEnumerable<>).MakeGenericType(queryableType.GetGenericArguments()); + MethodInfo asQueryableMethod = typeof(Queryable).GetMethod("AsQueryable", new[] { argumentType }); + return asQueryableMethod.Invoke(null, new[] { list }); + } + + return Queryable.AsQueryable((IEnumerable)list); + } + + private static object GenerateCollection(Type collectionType, int size, Dictionary createdObjectReferences) + { + Type type = collectionType.IsGenericType ? + collectionType.GetGenericArguments()[0] : + typeof(object); + object result = Activator.CreateInstance(collectionType); + MethodInfo addMethod = collectionType.GetMethod("Add"); + bool areAllElementsNull = true; + ObjectGenerator objectGenerator = new ObjectGenerator(); + for (int i = 0; i < size; i++) + { + object element = objectGenerator.GenerateObject(type, createdObjectReferences); + addMethod.Invoke(result, new object[] { element }); + areAllElementsNull &= element == null; + } + + if (areAllElementsNull) + { + return null; + } + + return result; + } + + private static object GenerateNullable(Type nullableType, Dictionary createdObjectReferences) + { + Type type = nullableType.GetGenericArguments()[0]; + ObjectGenerator objectGenerator = new ObjectGenerator(); + return objectGenerator.GenerateObject(type, createdObjectReferences); + } + + private static object GenerateComplexObject(Type type, Dictionary createdObjectReferences) + { + object result = null; + + if (createdObjectReferences.TryGetValue(type, out result)) + { + // The object has been created already, just return it. This will handle the circular reference case. + return result; + } + + if (type.IsValueType) + { + result = Activator.CreateInstance(type); + } + else + { + ConstructorInfo defaultCtor = type.GetConstructor(Type.EmptyTypes); + if (defaultCtor == null) + { + // Cannot instantiate the type because it doesn't have a default constructor + return null; + } + + result = defaultCtor.Invoke(new object[0]); + } + createdObjectReferences.Add(type, result); + SetPublicProperties(type, result, createdObjectReferences); + SetPublicFields(type, result, createdObjectReferences); + return result; + } + + private static void SetPublicProperties(Type type, object obj, Dictionary createdObjectReferences) + { + PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + ObjectGenerator objectGenerator = new ObjectGenerator(); + foreach (PropertyInfo property in properties) + { + if (property.CanWrite) + { + object propertyValue = objectGenerator.GenerateObject(property.PropertyType, createdObjectReferences); + property.SetValue(obj, propertyValue, null); + } + } + } + + private static void SetPublicFields(Type type, object obj, Dictionary createdObjectReferences) + { + FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); + ObjectGenerator objectGenerator = new ObjectGenerator(); + foreach (FieldInfo field in fields) + { + object fieldValue = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences); + field.SetValue(obj, fieldValue); + } + } + + private class SimpleTypeObjectGenerator + { + private long _index = 0; + private static readonly Dictionary> DefaultGenerators = InitializeGenerators(); + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")] + private static Dictionary> InitializeGenerators() + { + return new Dictionary> + { + { typeof(Boolean), index => true }, + { typeof(Byte), index => (Byte)64 }, + { typeof(Char), index => (Char)65 }, + { typeof(DateTime), index => DateTime.Now }, + { typeof(DateTimeOffset), index => new DateTimeOffset(DateTime.Now) }, + { typeof(DBNull), index => DBNull.Value }, + { typeof(Decimal), index => (Decimal)index }, + { typeof(Double), index => (Double)(index + 0.1) }, + { typeof(Guid), index => Guid.NewGuid() }, + { typeof(Int16), index => (Int16)(index % Int16.MaxValue) }, + { typeof(Int32), index => (Int32)(index % Int32.MaxValue) }, + { typeof(Int64), index => (Int64)index }, + { typeof(Object), index => new object() }, + { typeof(SByte), index => (SByte)64 }, + { typeof(Single), index => (Single)(index + 0.1) }, + { + typeof(String), index => + { + return String.Format(CultureInfo.CurrentCulture, "sample string {0}", index); + } + }, + { + typeof(TimeSpan), index => + { + return TimeSpan.FromTicks(1234567); + } + }, + { typeof(UInt16), index => (UInt16)(index % UInt16.MaxValue) }, + { typeof(UInt32), index => (UInt32)(index % UInt32.MaxValue) }, + { typeof(UInt64), index => (UInt64)index }, + { + typeof(Uri), index => + { + return new Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index)); + } + }, + }; + } + + public static bool CanGenerateObject(Type type) + { + return DefaultGenerators.ContainsKey(type); + } + + public object GenerateObject(Type type) + { + return DefaultGenerators[type](++_index); + } + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/SampleDirection.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/SampleDirection.cs new file mode 100644 index 000000000..41c9c5193 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/SampleDirection.cs @@ -0,0 +1,11 @@ +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// Indicates whether the sample is used for request or response + /// + public enum SampleDirection + { + Request = 0, + Response + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/TextSample.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/TextSample.cs new file mode 100644 index 000000000..328e64257 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/SampleGeneration/TextSample.cs @@ -0,0 +1,37 @@ +using System; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. + /// + public class TextSample + { + public TextSample(string text) + { + if (text == null) + { + throw new ArgumentNullException("text"); + } + Text = text; + } + + public string Text { get; private set; } + + public override bool Equals(object obj) + { + TextSample other = obj as TextSample; + return other != null && Text == other.Text; + } + + public override int GetHashCode() + { + return Text.GetHashCode(); + } + + public override string ToString() + { + return Text; + } + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Api.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Api.cshtml new file mode 100644 index 000000000..8feba5460 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Api.cshtml @@ -0,0 +1,22 @@ +@using System.Web.Http +@using OwinWebApi.Areas.HelpPage.Models +@model HelpPageApiModel + +@{ + var description = Model.ApiDescription; + ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath; +} + + +
+ +
+ @Html.DisplayForModel() +
+
diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml new file mode 100644 index 000000000..65724e38f --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml @@ -0,0 +1,41 @@ +@using System.Web.Http +@using System.Web.Http.Controllers +@using System.Web.Http.Description +@using OwinWebApi.Areas.HelpPage +@using OwinWebApi.Areas.HelpPage.Models +@model IGrouping + +@{ + var controllerDocumentation = ViewBag.DocumentationProvider != null ? + ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : + null; +} + +

@Model.Key.ControllerName

+@if (!String.IsNullOrEmpty(controllerDocumentation)) +{ +

@controllerDocumentation

+} + + + + + + @foreach (var api in Model) + { + + + + + } + +
APIDescription
@api.HttpMethod.Method @api.RelativePath + @if (api.Documentation != null) + { +

@api.Documentation

+ } + else + { +

No documentation available.

+ } +
\ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml new file mode 100644 index 000000000..2dd962255 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml @@ -0,0 +1,6 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model CollectionModelDescription +@if (Model.ElementDescription is ComplexTypeModelDescription) +{ + @Html.DisplayFor(m => m.ElementDescription) +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml new file mode 100644 index 000000000..7ea80d32d --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml @@ -0,0 +1,3 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model ComplexTypeModelDescription +@Html.DisplayFor(m => m.Properties, "Parameters") \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml new file mode 100644 index 000000000..2bb025ce7 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml @@ -0,0 +1,4 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model DictionaryModelDescription +Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] +and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml new file mode 100644 index 000000000..fa17430ce --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml @@ -0,0 +1,24 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model EnumTypeModelDescription + +

Possible enumeration values:

+ + + + + + + @foreach (EnumValueDescription value in Model.Values) + { + + + + + + } + +
NameValueDescription
@value.Name +

@value.Value

+
+

@value.Documentation

+
\ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml new file mode 100644 index 000000000..c1562f1a5 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml @@ -0,0 +1,67 @@ +@using System.Web.Http +@using System.Web.Http.Description +@using OwinWebApi.Areas.HelpPage.Models +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model HelpPageApiModel + +@{ + ApiDescription description = Model.ApiDescription; +} +

@description.HttpMethod.Method @description.RelativePath

+
+

@description.Documentation

+ +

Request Information

+ +

URI Parameters

+ @Html.DisplayFor(m => m.UriParameters, "Parameters") + +

Body Parameters

+ +

@Model.RequestDocumentation

+ + @if (Model.RequestModelDescription != null) + { + @Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription }) + if (Model.RequestBodyParameters != null) + { + @Html.DisplayFor(m => m.RequestBodyParameters, "Parameters") + } + } + else + { +

None.

+ } + + @if (Model.SampleRequests.Count > 0) + { +

Request Formats

+ @Html.DisplayFor(m => m.SampleRequests, "Samples") + } + +

Response Information

+ +

Resource Description

+ +

@description.ResponseDescription.Documentation

+ + @if (Model.ResourceDescription != null) + { + @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }) + if (Model.ResourceProperties != null) + { + @Html.DisplayFor(m => m.ResourceProperties, "Parameters") + } + } + else + { +

None.

+ } + + @if (Model.SampleResponses.Count > 0) + { +

Response Formats

+ @Html.DisplayFor(m => m.SampleResponses, "Samples") + } + +
\ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml new file mode 100644 index 000000000..aa1ebeec2 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml @@ -0,0 +1,4 @@ +@using OwinWebApi.Areas.HelpPage +@model ImageSample + + \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml new file mode 100644 index 000000000..3be808576 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml @@ -0,0 +1,13 @@ +@using OwinWebApi.Areas.HelpPage +@model InvalidSample + +@if (HttpContext.Current.IsDebuggingEnabled) +{ +
+

@Model.ErrorMessage

+
+} +else +{ +

Sample not available.

+} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml new file mode 100644 index 000000000..089ea6d8e --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml @@ -0,0 +1,4 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model KeyValuePairModelDescription +Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] +and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml new file mode 100644 index 000000000..4a36a9a7a --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml @@ -0,0 +1,26 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model Type +@{ + ModelDescription modelDescription = ViewBag.modelDescription; + if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription) + { + if (Model == typeof(Object)) + { + @:Object + } + else + { + @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null) + } + } + else if (modelDescription is CollectionModelDescription) + { + var collectionDescription = modelDescription as CollectionModelDescription; + var elementDescription = collectionDescription.ElementDescription; + @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) + } + else + { + @Html.DisplayFor(m => modelDescription) + } +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml new file mode 100644 index 000000000..9ac128338 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml @@ -0,0 +1,48 @@ +@using System.Collections.Generic +@using System.Collections.ObjectModel +@using System.Web.Http.Description +@using System.Threading +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model IList + +@if (Model.Count > 0) +{ + + + + + + @foreach (ParameterDescription parameter in Model) + { + ModelDescription modelDescription = parameter.TypeDescription; + + + + + + + } + +
NameDescriptionTypeAdditional information
@parameter.Name +

@parameter.Documentation

+
+ @Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription }) + + @if (parameter.Annotations.Count > 0) + { + foreach (var annotation in parameter.Annotations) + { +

@annotation.Documentation

+ } + } + else + { +

None.

+ } +
+} +else +{ +

None.

+} + diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml new file mode 100644 index 000000000..c19596fb1 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml @@ -0,0 +1,30 @@ +@using System.Net.Http.Headers +@model Dictionary + +@{ + // Group the samples into a single tab if they are the same. + Dictionary samples = Model.GroupBy(pair => pair.Value).ToDictionary( + pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()), + pair => pair.Key); + var mediaTypes = samples.Keys; +} +
+ @foreach (var mediaType in mediaTypes) + { +

@mediaType

+
+ Sample: + @{ + var sample = samples[mediaType]; + if (sample == null) + { +

Sample not available.

+ } + else + { + @Html.DisplayFor(s => sample); + } + } +
+ } +
\ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml new file mode 100644 index 000000000..2ef4bd236 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml @@ -0,0 +1,3 @@ +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model SimpleTypeModelDescription +@Model.Documentation \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml new file mode 100644 index 000000000..0574eae41 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml @@ -0,0 +1,6 @@ +@using OwinWebApi.Areas.HelpPage +@model TextSample + +
+@Model.Text
+
\ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Index.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Index.cshtml new file mode 100644 index 000000000..1fd225c37 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/Index.cshtml @@ -0,0 +1,38 @@ +@using System.Web.Http +@using System.Web.Http.Controllers +@using System.Web.Http.Description +@using System.Collections.ObjectModel +@using OwinWebApi.Areas.HelpPage.Models +@model Collection + +@{ + ViewBag.Title = "ASP.NET Web API Help Page"; + + // Group APIs by controller + ILookup apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor); +} + + +
+
+
+

@ViewBag.Title

+
+
+
+
+ +
+ @foreach (var group in apiGroups) + { + @Html.DisplayFor(m => group, "ApiGroup") + } +
+
diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/ResourceModel.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/ResourceModel.cshtml new file mode 100644 index 000000000..60b29a11b --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Help/ResourceModel.cshtml @@ -0,0 +1,19 @@ +@using System.Web.Http +@using OwinWebApi.Areas.HelpPage.ModelDescriptions +@model ModelDescription + + +
+ +

@Model.Name

+

@Model.Documentation

+
+ @Html.DisplayFor(m => Model) +
+
diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Shared/_Layout.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Shared/_Layout.cshtml new file mode 100644 index 000000000..896c833a0 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Shared/_Layout.cshtml @@ -0,0 +1,12 @@ + + + + + + @ViewBag.Title + @RenderSection("scripts", required: false) + + + @RenderBody() + + \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Web.config b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Web.config new file mode 100644 index 000000000..820e75486 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/Web.config @@ -0,0 +1,41 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/_ViewStart.cshtml b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/_ViewStart.cshtml new file mode 100644 index 000000000..d735b1cb0 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/Views/_ViewStart.cshtml @@ -0,0 +1,4 @@ +@{ + // Change the Layout path below to blend the look and feel of the help page with your existing web pages + Layout = "~/Views/Shared/_Layout.cshtml"; +} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/XmlDocumentationProvider.cs b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/XmlDocumentationProvider.cs new file mode 100644 index 000000000..87a0f0716 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Areas/HelpPage/XmlDocumentationProvider.cs @@ -0,0 +1,161 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Web.Http.Controllers; +using System.Web.Http.Description; +using System.Xml.XPath; +using OwinWebApi.Areas.HelpPage.ModelDescriptions; + +namespace OwinWebApi.Areas.HelpPage +{ + /// + /// A custom that reads the API documentation from an XML documentation file. + /// + public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider + { + private XPathNavigator _documentNavigator; + private const string TypeExpression = "/doc/members/member[@name='T:{0}']"; + private const string MethodExpression = "/doc/members/member[@name='M:{0}']"; + private const string PropertyExpression = "/doc/members/member[@name='P:{0}']"; + private const string FieldExpression = "/doc/members/member[@name='F:{0}']"; + private const string ParameterExpression = "param[@name='{0}']"; + + /// + /// Initializes a new instance of the class. + /// + /// The physical path to XML document. + public XmlDocumentationProvider(string documentPath) + { + if (documentPath == null) + { + throw new ArgumentNullException("documentPath"); + } + XPathDocument xpath = new XPathDocument(documentPath); + _documentNavigator = xpath.CreateNavigator(); + } + + public string GetDocumentation(HttpControllerDescriptor controllerDescriptor) + { + XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType); + return GetTagValue(typeNode, "summary"); + } + + public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) + { + XPathNavigator methodNode = GetMethodNode(actionDescriptor); + return GetTagValue(methodNode, "summary"); + } + + public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) + { + ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor; + if (reflectedParameterDescriptor != null) + { + XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor); + if (methodNode != null) + { + string parameterName = reflectedParameterDescriptor.ParameterInfo.Name; + XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)); + if (parameterNode != null) + { + return parameterNode.Value.Trim(); + } + } + } + + return null; + } + + public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor) + { + XPathNavigator methodNode = GetMethodNode(actionDescriptor); + return GetTagValue(methodNode, "returns"); + } + + public string GetDocumentation(MemberInfo member) + { + string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name); + string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression; + string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName); + XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression); + return GetTagValue(propertyNode, "summary"); + } + + public string GetDocumentation(Type type) + { + XPathNavigator typeNode = GetTypeNode(type); + return GetTagValue(typeNode, "summary"); + } + + private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor) + { + ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; + if (reflectedActionDescriptor != null) + { + string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); + return _documentNavigator.SelectSingleNode(selectExpression); + } + + return null; + } + + private static string GetMemberName(MethodInfo method) + { + string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name); + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length != 0) + { + string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray(); + name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)); + } + + return name; + } + + private static string GetTagValue(XPathNavigator parentNode, string tagName) + { + if (parentNode != null) + { + XPathNavigator node = parentNode.SelectSingleNode(tagName); + if (node != null) + { + return node.Value.Trim(); + } + } + + return null; + } + + private XPathNavigator GetTypeNode(Type type) + { + string controllerTypeName = GetTypeName(type); + string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName); + return _documentNavigator.SelectSingleNode(selectExpression); + } + + private static string GetTypeName(Type type) + { + string name = type.FullName; + if (type.IsGenericType) + { + // Format the generic type name to something like: Generic{System.Int32,System.String} + Type genericType = type.GetGenericTypeDefinition(); + Type[] genericArguments = type.GetGenericArguments(); + string genericTypeName = genericType.FullName; + + // Trim the generic parameter counts from the name + genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); + string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray(); + name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames)); + } + if (type.IsNested) + { + // Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. + name = name.Replace("+", "."); + } + + return name; + } + } +} diff --git a/tests/aspnet-mvc/OwinWebApi/Content/Site.css b/tests/aspnet-mvc/OwinWebApi/Content/Site.css new file mode 100644 index 000000000..d825a52a8 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/Site.css @@ -0,0 +1,17 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Set padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Set width on the form input elements since they're 100% wide by default */ +input, +select, +textarea { + max-width: 280px; +} diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css new file mode 100644 index 000000000..ea33f76a7 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + text-shadow: 0 1px 0 #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; + background-color: #e8e8e8; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + background-color: #2e6da4; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css.map b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css.map new file mode 100644 index 000000000..949d09738 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACiBH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFzDT;ACkBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CF1CT;ACQC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFrBT;AC7BD;;;;;;EAuBI,kBAAA;CDcH;AC2BC;;EAEE,uBAAA;CDzBH;AC8BD;EEvEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;EAyCA,0BAAA;EACA,mBAAA;CDtBD;AClBC;;EAEE,0BAAA;EACA,6BAAA;CDoBH;ACjBC;;EAEE,0BAAA;EACA,sBAAA;CDmBH;ACbG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD2BL;ACPD;EE5EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;CD4DD;AC1DC;;EAEE,0BAAA;EACA,6BAAA;CD4DH;ACzDC;;EAEE,0BAAA;EACA,sBAAA;CD2DH;ACrDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDmEL;AC9CD;EE7EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;CDoGD;AClGC;;EAEE,0BAAA;EACA,6BAAA;CDoGH;ACjGC;;EAEE,0BAAA;EACA,sBAAA;CDmGH;AC7FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD2GL;ACrFD;EE9EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;CD4ID;AC1IC;;EAEE,0BAAA;EACA,6BAAA;CD4IH;ACzIC;;EAEE,0BAAA;EACA,sBAAA;CD2IH;ACrIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDmJL;AC5HD;EE/EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;CDoLD;AClLC;;EAEE,0BAAA;EACA,6BAAA;CDoLH;ACjLC;;EAEE,0BAAA;EACA,sBAAA;CDmLH;AC7KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD2LL;ACnKD;EEhFI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EClBF,oEAAA;EH8CA,4BAAA;EACA,sBAAA;CD4ND;AC1NC;;EAEE,0BAAA;EACA,6BAAA;CD4NH;ACzNC;;EAEE,0BAAA;EACA,sBAAA;CD2NH;ACrNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDmOL;ACpMD;;ECtCE,mDAAA;EACQ,2CAAA;CF8OT;AC/LD;;EEjGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFgGF,0BAAA;CDqMD;ACnMD;;;EEtGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFsGF,0BAAA;CDyMD;AChMD;EEnHI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;ECnBF,oEAAA;EHqIA,mBAAA;ECrEA,4FAAA;EACQ,oFAAA;CF4QT;AC3MD;;EEnHI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;ED6CF,yDAAA;EACQ,iDAAA;CFsRT;ACxMD;;EAEE,+CAAA;CD0MD;ACtMD;EEtII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,uHAAA;EACA,4BAAA;ECnBF,oEAAA;EHwJA,mBAAA;CD4MD;AC/MD;;EEtII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;ED6CF,wDAAA;EACQ,gDAAA;CF6ST;ACzND;;EAYI,0CAAA;CDiNH;AC5MD;;;EAGE,iBAAA;CD8MD;AC1MD;EAEI;;;IAGE,YAAA;IEnKF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,uHAAA;IACA,4BAAA;GH+WD;CACF;ACrMD;EACE,8CAAA;EC/HA,2FAAA;EACQ,mFAAA;CFuUT;AC7LD;EE5LI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFoLF,sBAAA;CDyMD;ACpMD;EE7LI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFoLF,sBAAA;CDiND;AC3MD;EE9LI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFoLF,sBAAA;CDyND;AClND;EE/LI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFoLF,sBAAA;CDiOD;AClND;EEvMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CH4ZH;AC/MD;EEjNI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHmaH;ACrND;EElNI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CH0aH;AC3ND;EEnNI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHibH;ACjOD;EEpNI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHwbH;ACvOD;EErNI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CH+bH;AC1OD;EExLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;ACtOD;EACE,mBAAA;EClLA,mDAAA;EACQ,2CAAA;CF2ZT;ACvOD;;;EAGE,8BAAA;EEzOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EFuOF,sBAAA;CD6OD;AClPD;;;EAQI,kBAAA;CD+OH;ACrOD;ECvME,kDAAA;EACQ,0CAAA;CF+aT;AC/ND;EElQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHoeH;ACrOD;EEnQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CH2eH;AC3OD;EEpQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHkfH;ACjPD;EErQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHyfH;ACvPD;EEtQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHggBH;AC7PD;EEvQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;CHugBH;AC7PD;EE9QI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,uHAAA;EACA,4BAAA;EF4QF,sBAAA;EC/NA,0FAAA;EACQ,kFAAA;CFmeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n background-repeat: repeat-x;\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n background-repeat: repeat-x;\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n background-repeat: repeat-x;\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","// stylelint-disable selector-no-qualifying-type, selector-max-compound-selectors\n\n/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default {\n .btn-styles(@btn-default-bg);\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0, 0, 0, .075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0, 0, 0, .075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, .25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0, 0, 0, .25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, .2);\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0, 0, 0, .075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0, 0, 0, .05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);\n .box-shadow(@shadow);\n}\n","// stylelint-disable indentation, property-no-vendor-prefix, selector-no-vendor-prefix\n\n// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n word-wrap: break-word;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// stylelint-disable value-no-vendor-prefix, selector-max-id\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\", argb(@start-color), argb(@end-color))); // IE9 and down\n background-repeat: repeat-x;\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\", argb(@start-color), argb(@end-color))); // IE9 and down\n background-repeat: repeat-x;\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n background-repeat: no-repeat;\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n background-repeat: no-repeat;\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255, 255, 255, .15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css new file mode 100644 index 000000000..2a69f48c7 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x;background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x;background-color:#2e6da4}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css.map b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css.map new file mode 100644 index 000000000..5d75106e0 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap-theme.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","dist/css/bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n background-repeat: repeat-x;\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n background-repeat: repeat-x;\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n background-repeat: repeat-x;\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n background-repeat: repeat-x;\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n background-repeat: repeat-x;\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n background-repeat: repeat-x;\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n background-repeat: repeat-x;\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n background-repeat: repeat-x;\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n background-repeat: repeat-x;\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n background-repeat: repeat-x;\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n background-repeat: repeat-x;\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","// stylelint-disable selector-no-qualifying-type, selector-max-compound-selectors\n\n/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default {\n .btn-styles(@btn-default-bg);\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0, 0, 0, .075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0, 0, 0, .075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, .25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0, 0, 0, .25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, .2);\n @shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0, 0, 0, .075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0, 0, 0, .05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);\n .box-shadow(@shadow);\n}\n","// stylelint-disable indentation, property-no-vendor-prefix, selector-no-vendor-prefix\n\n// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n word-wrap: break-word;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// stylelint-disable value-no-vendor-prefix, selector-max-id\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\", argb(@start-color), argb(@end-color))); // IE9 and down\n background-repeat: repeat-x;\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\", argb(@start-color), argb(@end-color))); // IE9 and down\n background-repeat: repeat-x;\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n background-repeat: no-repeat;\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n background-repeat: no-repeat;\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255, 255, 255, .15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css new file mode 100644 index 000000000..fcab41554 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css @@ -0,0 +1,6834 @@ +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: none; + text-decoration: underline; + -webkit-text-decoration: underline dotted; + -moz-text-decoration: underline dotted; + text-decoration: underline dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +mark { + background: #ff0; + color: #000; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: "Glyphicons Halflings"; + src: url("../fonts/glyphicons-halflings-regular.eot"); + src: url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"), url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"), url("../fonts/glyphicons-halflings-regular.woff") format("woff"), url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"), url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg"); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: "Glyphicons Halflings"; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: 400; + line-height: 1; + color: #777777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: 700; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eeeeee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: "\2014 \00A0"; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eeeeee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ""; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: "\00A0 \2014"; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.row-no-gutters { + margin-right: 0; + margin-left: 0; +} +.row-no-gutters [class*="col-"] { + padding-right: 0; + padding-left: 0; +} +.col-xs-1, +.col-sm-1, +.col-md-1, +.col-lg-1, +.col-xs-2, +.col-sm-2, +.col-md-2, +.col-lg-2, +.col-xs-3, +.col-sm-3, +.col-md-3, +.col-lg-3, +.col-xs-4, +.col-sm-4, +.col-md-4, +.col-lg-4, +.col-xs-5, +.col-sm-5, +.col-md-5, +.col-lg-5, +.col-xs-6, +.col-sm-6, +.col-md-6, +.col-lg-6, +.col-xs-7, +.col-sm-7, +.col-md-7, +.col-lg-7, +.col-xs-8, +.col-sm-8, +.col-md-8, +.col-lg-8, +.col-xs-9, +.col-sm-9, +.col-md-9, +.col-lg-9, +.col-xs-10, +.col-sm-10, +.col-md-10, +.col-lg-10, +.col-xs-11, +.col-sm-11, +.col-md-11, +.col-lg-11, +.col-xs-12, +.col-sm-12, +.col-md-12, +.col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, +.col-xs-2, +.col-xs-3, +.col-xs-4, +.col-xs-5, +.col-xs-6, +.col-xs-7, +.col-xs-8, +.col-xs-9, +.col-xs-10, +.col-xs-11, +.col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0%; +} +@media (min-width: 768px) { + .col-sm-1, + .col-sm-2, + .col-sm-3, + .col-sm-4, + .col-sm-5, + .col-sm-6, + .col-sm-7, + .col-sm-8, + .col-sm-9, + .col-sm-10, + .col-sm-11, + .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 992px) { + .col-md-1, + .col-md-2, + .col-md-3, + .col-md-4, + .col-md-5, + .col-md-6, + .col-md-7, + .col-md-8, + .col-md-9, + .col-md-10, + .col-md-11, + .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 1200px) { + .col-lg-1, + .col-lg-2, + .col-lg-3, + .col-lg-4, + .col-lg-5, + .col-lg-6, + .col-lg-7, + .col-lg-8, + .col-lg-9, + .col-lg-10, + .col-lg-11, + .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0%; + } +} +table { + background-color: transparent; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: 0.01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: 700; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eeeeee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + vertical-align: middle; + cursor: pointer; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + opacity: 0.65; + -webkit-box-shadow: none; + box-shadow: none; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + background-image: none; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + background-image: none; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + background-image: none; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + background-image: none; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + background-image: none; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + background-image: none; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: 400; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; + -webkit-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + transition-duration: 0.35s; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #555555; + text-align: center; + background-color: #eeeeee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #777777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-right: 15px; + margin-top: 8px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-right: -15px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 8px; + margin-bottom: 8px; +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eeeeee; + border-color: #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: 0.2em 0.6em 0.3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eeeeee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border 0.2s ease-in-out; + -o-transition: border 0.2s ease-in-out; + transition: border 0.2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777777; + cursor: not-allowed; + background-color: #eeeeee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: 0.2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: 0.5; +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: -webkit-transform 0.3s ease-out; + transition: transform 0.3s ease-out; + transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out, -o-transform 0.3s ease-out; +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + outline: 0; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: 0.5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: 400; + line-height: 1.42857143; + line-break: auto; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + font-size: 12px; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: 0.9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: 400; + line-height: 1.42857143; + line-break: auto; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + font-size: 14px; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999999; + border-bottom-color: rgba(0, 0, 0, 0.25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform 0.6s ease-in-out; + -o-transition: -o-transform 0.6s ease-in-out; + transition: -webkit-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out, -o-transform 0.6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + left: 0; + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + left: 0; + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + left: 0; + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: 0.5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + outline: 0; + filter: alpha(opacity=90); + opacity: 0.9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: "\2039"; +} +.carousel-control .icon-next:before { + content: "\203a"; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css.map b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css.map new file mode 100644 index 000000000..caac3e612 --- /dev/null +++ b/tests/aspnet-mvc/OwinWebApi/Content/bootstrap.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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A;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;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rDA;EC8DG,oBAAA;CfwnDH;ActrDA;EC8DG,oBAAA;Cf2nDH;AczrDA;EC8DG,WAAA;Cf8nDH;Ac5rDA;EC8DG,oBAAA;CfioDH;Ac/rDA;EC8DG,oBAAA;CfooDH;AclsDA;EC8DG,WAAA;CfuoDH;AcrsDA;EC8DG,oBAAA;Cf0oDH;AcxsDA;EC8DG,oBAAA;Cf6oDH;Ac3sDA;EC8DG,WAAA;CfgpDH;Ac9sDA;EC8DG,oBAAA;CfmpDH;AcjtDA;EC8DG,mBAAA;CfspDH;AcptDA;ECmEG,YAAA;CfopDH;AcvtDA;ECoDG,WAAA;CfsqDH;Ac1tDA;ECoDG,mBAAA;CfyqDH;Ac7tDA;ECoDG,mBAAA;Cf4qDH;AchuDA;ECoDG,UAAA;Cf+qDH;AcnuDA;ECoDG,mBAAA;CfkrDH;ActuDA;ECoDG,mBAAA;CfqrDH;AczuDA;ECoDG,UAAA;CfwrDH;Ac5uDA;ECoDG,mBAAA;Cf2rDH;Ac/uDA;ECoDG,mBAAA;Cf8rDH;AclvDA;ECoDG,UAAA;CfisDH;AcrvDA;ECoDG,mBAAA;CfosDH;AcxvDA;ECoDG,kBAAA;CfusDH;Ac3vDA;ECyDG,WAAA;CfqsDH;Ac9vDA;ECwEG,kBAAA;CfyrDH;AcjwDA;ECwEG,0BAAA;Cf4rDH;AcpwDA;ECwEG,0BAAA;Cf+rDH;AcvwDA;ECwEG,iBAAA;CfksDH;Ac1wDA;ECwEG,0BAAA;CfqsDH;Ac7wDA;ECwEG,0BAAA;CfwsDH;AchxDA;ECwEG,iBAAA;Cf2sDH;AcnxDA;ECwEG,0BAAA;Cf8sDH;ActxDA;ECwEG,0BAAA;CfitDH;AczxDA;ECwEG,iBAAA;CfotDH;Ac5xDA;ECwEG,0BAAA;CfutDH;Ac/xDA;ECwEG,yBAAA;Cf0tDH;AclyDA;ECwEG,gBAAA;Cf6tDH;Aa5tDD;ECzEC;;;;;;;;;;;;ICuCK,YAAA;Gf6wDH;EcpzDF;IC+CG,YAAA;GfwwDD;EcvzDF;IC+CG,oBAAA;Gf2wDD;Ec1zDF;IC+CG,oBAAA;Gf8wDD;Ec7zDF;IC+CG,WAAA;GfixDD;Ech0DF;IC+CG,oBAAA;GfoxDD;Ecn0DF;IC+CG,oBAAA;GfuxDD;Ect0DF;IC+CG,WAAA;Gf0xDD;Ecz0DF;IC+CG,oBAAA;Gf6xDD;Ec50DF;IC+CG,oBAAA;GfgyDD;Ec/0DF;IC+CG,WAAA;GfmyDD;Ecl1DF;IC+CG,oBAAA;GfsyDD;Ecr1DF;IC+CG,mBAAA;GfyyDD;Ecx1DF;IC8DG,YAAA;Gf6xDD;Ec31DF;IC8DG,oBAAA;GfgyDD;Ec91DF;IC8DG,oBAAA;GfmyDD;Ecj2DF;IC8DG,WAAA;GfsyDD;Ecp2DF;IC8DG,oBAAA;GfyyDD;Ecv2DF;IC8DG,oBAAA;Gf4yDD;Ec12DF;IC8DG,WAAA;Gf+yDD;Ec72DF;IC8DG,oBAAA;GfkzDD;Ech3DF;IC8DG,oBAAA;GfqzDD;Ecn3DF;IC8DG,WAAA;GfwzDD;Ect3DF;IC8DG,oBAAA;Gf2zDD;Ecz3DF;IC8DG,mBAAA;Gf8zDD;Ec53DF;ICmEG,YAAA;Gf4zDD;Ec/3DF;ICoDG,WAAA;Gf80DD;Ecl4DF;ICoDG,mBAAA;Gfi1DD;Ecr4DF;ICoDG,mBAAA;Gfo1DD;Ecx4DF;ICoDG,UAAA;Gfu1DD;Ec34DF;ICoDG,mBAAA;Gf01DD;Ec94DF;ICoDG,mBAAA;Gf61DD;Ecj5DF;ICoDG,UAAA;Gfg2DD;Ecp5DF;ICoDG,mBAAA;Gfm2DD;Ecv5DF;ICoDG,mBAAA;Gfs2DD;Ec15DF;ICoDG,UAAA;Gfy2DD;Ec75DF;ICoDG,mBAAA;Gf42DD;Ech6DF;ICoDG,kBAAA;Gf+2DD;Ecn6DF;ICyDG,WAAA;Gf62DD;Ect6DF;ICwEG,kBAAA;Gfi2DD;Ecz6DF;ICwEG,0BAAA;Gfo2DD;Ec56DF;ICwEG,0BAAA;Gfu2DD;Ec/6DF;ICwEG,iBAAA;Gf02DD;Ecl7DF;ICwEG,0BAAA;Gf62DD;Ecr7DF;ICwEG,0BAAA;Gfg3DD;Ecx7DF;ICwEG,iBAAA;Gfm3DD;Ec37DF;ICwEG,0BAAA;Gfs3DD;Ec97DF;ICwEG,0BAAA;Gfy3DD;Ecj8DF;ICwEG,iBAAA;Gf43DD;Ecp8DF;ICwEG,0BAAA;Gf+3DD;Ecv8DF;ICwEG,yBAAA;Gfk4DD;Ec18DF;ICwEG,gBAAA;Gfq4DD;CACF;Aa53DD;EClFC;;;;;;;;;;;;ICuCK,YAAA;Gfs7DH;Ec79DF;IC+CG,YAAA;Gfi7DD;Ech+DF;IC+CG,oBAAA;Gfo7DD;Ecn+DF;IC+CG,oBAAA;Gfu7DD;Ect+DF;IC+CG,WAAA;Gf07DD;Ecz+DF;IC+CG,oBAAA;Gf67DD;Ec5+DF;IC+CG,oBAAA;Gfg8DD;Ec/+DF;IC+CG,WAAA;Gfm8DD;Ecl/DF;IC+CG,oBAAA;Gfs8DD;Ecr/DF;IC+CG,oBAAA;Gfy8DD;Ecx/DF;IC+CG,WAAA;Gf48DD;Ec3/DF;IC+CG,oBAAA;Gf+8DD;Ec9/DF;IC+CG,mBAAA;Gfk9DD;EcjgEF;IC8DG,YAAA;Gfs8DD;EcpgEF;IC8DG,oBAAA;Gfy8DD;EcvgEF;IC8DG,oBAAA;Gf48DD;Ec1gEF;IC8DG,WAAA;Gf+8DD;Ec7gEF;IC8DG,oBAAA;Gfk9DD;EchhEF;IC8DG,oBAAA;Gfq9DD;EcnhEF;IC8DG,WAAA;Gfw9DD;EcthEF;IC8DG,oBAAA;Gf29DD;EczhEF;IC8DG,oBAAA;Gf89DD;Ec5hEF;IC8DG,WAAA;Gfi+DD;Ec/hEF;IC8DG,oBAAA;Gfo+DD;EcliEF;IC8DG,mBAAA;Gfu+DD;EcriEF;ICmEG,YAAA;Gfq+DD;EcxiEF;ICoDG,WAAA;Gfu/DD;Ec3iEF;ICoDG,mBAAA;Gf0/DD;Ec9iEF;ICoDG,mBAAA;Gf6/DD;EcjjEF;ICoDG,UAAA;GfggED;EcpjEF;ICoDG,mBAAA;GfmgED;EcvjEF;ICoDG,mBAAA;GfsgED;Ec1jEF;ICoDG,UAAA;GfygED;Ec7jEF;ICoDG,mBAAA;Gf4gED;EchkEF;ICoDG,mBAAA;Gf+gED;EcnkEF;ICoDG,UAAA;GfkhED;EctkEF;ICoDG,mBAAA;GfqhED;EczkEF;ICoDG,kBAAA;GfwhED;Ec5kEF;ICyDG,WAAA;GfshED;Ec/kEF;ICwEG,kBAAA;Gf0gED;EcllEF;ICwEG,0BAAA;Gf6gED;EcrlEF;ICwEG,0BAAA;GfghED;EcxlEF;ICwEG,iBAAA;GfmhED;Ec3lEF;ICwEG,0BAAA;GfshED;Ec9lEF;ICwEG,0BAAA;GfyhED;EcjmEF;ICwEG,iBAAA;Gf4hED;EcpmEF;ICwEG,0BAAA;Gf+hED;EcvmEF;ICwEG,0BAAA;GfkiED;Ec1mEF;ICwEG,iBAAA;GfqiED;Ec7mEF;ICwEG,0BAAA;GfwiED;EchnEF;ICwEG,yBAAA;Gf2iED;EcnnEF;ICwEG,gBAAA;Gf8iED;CACF;Aa5hED;EC3FC;;;;;;;;;;;;ICuCK,YAAA;Gf+lEH;EctoEF;IC+CG,YAAA;Gf0lED;EczoEF;IC+CG,oBAAA;Gf6lED;Ec5oEF;IC+CG,oBAAA;GfgmED;Ec/oEF;IC+CG,WAAA;GfmmED;EclpEF;IC+CG,oBAAA;GfsmED;EcrpEF;IC+CG,oBAAA;GfymED;EcxpEF;IC+CG,WAAA;Gf4mED;Ec3pEF;IC+CG,oBAAA;Gf+mED;Ec9pEF;IC+CG,oBAAA;GfknED;EcjqEF;IC+CG,WAAA;GfqnED;EcpqEF;IC+CG,oBAAA;GfwnED;EcvqEF;IC+CG,mBAAA;Gf2nED;Ec1qEF;IC8DG,YAAA;Gf+mED;Ec7qEF;IC8DG,oBAAA;GfknED;EchrEF;IC8DG,oBAAA;GfqnED;EcnrEF;IC8DG,WAAA;GfwnED;EctrEF;IC8DG,oBAAA;Gf2nED;EczrEF;IC8DG,oBAAA;Gf8nED;Ec5rEF;IC8DG,WAAA;GfioED;Ec/rEF;IC8DG,oBAAA;GfooED;EclsEF;IC8DG,oBAAA;GfuoED;EcrsEF;IC8DG,WAAA;Gf0oED;EcxsEF;IC8DG,oBAAA;Gf6oED;Ec3sEF;IC8DG,mBAAA;GfgpED;Ec9sEF;ICmEG,YAAA;Gf8oED;EcjtEF;ICoDG,WAAA;GfgqED;EcptEF;ICoDG,mBAAA;GfmqED;EcvtEF;ICoDG,mBAAA;GfsqED;Ec1tEF;ICoDG,UAAA;GfyqED;Ec7tEF;ICoDG,mBAAA;Gf4qED;EchuEF;ICoDG,mBAAA;Gf+qED;EcnuEF;ICoDG,UAAA;GfkrED;EctuEF;ICoDG,mBAAA;GfqrED;EczuEF;ICoDG,mBAAA;GfwrED;Ec5uEF;ICoDG,UAAA;Gf2rED;Ec/uEF;ICoDG,mBAAA;Gf8rED;EclvEF;ICoDG,kBAAA;GfisED;EcrvEF;ICyDG,WAAA;Gf+rED;EcxvEF;ICwEG,kBAAA;GfmrED;Ec3vEF;ICwEG,0BAAA;GfsrED;Ec9vEF;ICwEG,0BAAA;GfyrED;EcjwEF;ICwEG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","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: none;\n text-decoration: underline;\n text-decoration: underline dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n color: #000 !important;\n text-shadow: none !important;\n background: transparent !important;\n box-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: \"Glyphicons Halflings\";\n src: url(\"../fonts/glyphicons-halflings-regular.eot\");\n src: url(\"../fonts/glyphicons-halflings-regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"../fonts/glyphicons-halflings-regular.woff2\") format(\"woff2\"), url(\"../fonts/glyphicons-halflings-regular.woff\") format(\"woff\"), url(\"../fonts/glyphicons-halflings-regular.ttf\") format(\"truetype\"), url(\"../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular\") format(\"svg\");\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: \"Glyphicons Halflings\";\n font-style: normal;\n font-weight: 400;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: 400;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-right: 5px;\n padding-left: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: 700;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: \"\\2014 \\00A0\";\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n text-align: right;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: \"\";\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: \"\\00A0 \\2014\";\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n color: #333333;\n word-break: break-all;\n word-wrap: break-word;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n.row {\n margin-right: -15px;\n margin-left: -15px;\n}\n.row-no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n.row-no-gutters [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n.col-xs-1,\n.col-sm-1,\n.col-md-1,\n.col-lg-1,\n.col-xs-2,\n.col-sm-2,\n.col-md-2,\n.col-lg-2,\n.col-xs-3,\n.col-sm-3,\n.col-md-3,\n.col-lg-3,\n.col-xs-4,\n.col-sm-4,\n.col-md-4,\n.col-lg-4,\n.col-xs-5,\n.col-sm-5,\n.col-md-5,\n.col-lg-5,\n.col-xs-6,\n.col-sm-6,\n.col-md-6,\n.col-lg-6,\n.col-xs-7,\n.col-sm-7,\n.col-md-7,\n.col-lg-7,\n.col-xs-8,\n.col-sm-8,\n.col-md-8,\n.col-lg-8,\n.col-xs-9,\n.col-sm-9,\n.col-md-9,\n.col-lg-9,\n.col-xs-10,\n.col-sm-10,\n.col-md-10,\n.col-lg-10,\n.col-xs-11,\n.col-sm-11,\n.col-md-11,\n.col-lg-11,\n.col-xs-12,\n.col-sm-12,\n.col-md-12,\n.col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n.col-xs-1,\n.col-xs-2,\n.col-xs-3,\n.col-xs-4,\n.col-xs-5,\n.col-xs-6,\n.col-xs-7,\n.col-xs-8,\n.col-xs-9,\n.col-xs-10,\n.col-xs-11,\n.col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1,\n .col-sm-2,\n .col-sm-3,\n .col-sm-4,\n .col-sm-5,\n .col-sm-6,\n .col-sm-7,\n .col-sm-8,\n .col-sm-9,\n .col-sm-10,\n .col-sm-11,\n .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1,\n .col-md-2,\n .col-md-3,\n .col-md-4,\n .col-md-5,\n .col-md-6,\n .col-md-7,\n .col-md-8,\n .col-md-9,\n .col-md-10,\n .col-md-11,\n .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1,\n .col-lg-2,\n .col-lg-3,\n .col-lg-4,\n .col-lg-5,\n .col-lg-6,\n .col-lg-7,\n .col-lg-8,\n .col-lg-9,\n .col-lg-10,\n .col-lg-11,\n .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ntable col[class*=\"col-\"] {\n position: static;\n display: table-column;\n float: none;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n display: table-cell;\n float: none;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n min-height: 0.01%;\n overflow-x: auto;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: 700;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n -webkit-appearance: none;\n appearance: none;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: 400;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-top: 4px \\9;\n margin-left: -20px;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: 400;\n vertical-align: middle;\n cursor: pointer;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\n.form-control-static {\n min-height: 34px;\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-right: 0;\n padding-left: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #3c763d;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #8a6d3b;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n background-color: #f2dede;\n border-color: #a94442;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n padding-top: 7px;\n margin-top: 0;\n margin-bottom: 0;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n padding-top: 7px;\n margin-bottom: 0;\n text-align: right;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n outline: 0;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n filter: alpha(opacity=65);\n opacity: 0.65;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n background-image: none;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n background-image: none;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n background-image: none;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n background-image: none;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n background-image: none;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n background-image: none;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n font-weight: 400;\n color: #337ab7;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n font-size: 14px;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: 400;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n color: #262626;\n text-decoration: none;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n background-color: #337ab7;\n outline: 0;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n cursor: not-allowed;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu-left {\n right: auto;\n left: 0;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n content: \"\";\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n right: 0;\n left: auto;\n }\n .navbar-right .dropdown-menu-left {\n right: auto;\n left: 0;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-right: 8px;\n padding-left: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-right: 12px;\n padding-left: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n display: table-cell;\n float: none;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-right: 0;\n padding-left: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: 400;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n cursor: not-allowed;\n background-color: transparent;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n cursor: default;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n margin-bottom: 5px;\n text-align: center;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n margin-bottom: 5px;\n text-align: center;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n padding-right: 15px;\n padding-left: 15px;\n overflow-x: visible;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-right: 0;\n padding-left: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-brand {\n float: left;\n height: 50px;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n padding: 9px 10px;\n margin-right: 15px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n padding: 10px 15px;\n margin-right: -15px;\n margin-left: -15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n padding-top: 0;\n padding-bottom: 0;\n margin-right: 0;\n margin-left: 0;\n border: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-right: 15px;\n margin-left: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n color: #fff;\n background-color: #080808;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n padding: 0 5px;\n color: #ccc;\n content: \"/\\00a0\";\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n margin-left: -1px;\n line-height: 1.42857143;\n color: #337ab7;\n text-decoration: none;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-top-left-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n cursor: default;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n cursor: not-allowed;\n background-color: #fff;\n border-color: #ddd;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-top-left-radius: 6px;\n border-bottom-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-top-left-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-top-right-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n text-align: center;\n list-style: none;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n cursor: not-allowed;\n background-color: #fff;\n}\n.label {\n display: inline;\n padding: 0.2em 0.6em 0.3em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n padding-right: 15px;\n padding-left: 15px;\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-right: 60px;\n padding-left: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-right: auto;\n margin-left: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n height: 20px;\n margin-bottom: 20px;\n overflow: hidden;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n overflow: hidden;\n zoom: 1;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n padding-left: 0;\n margin-bottom: 20px;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n color: #777777;\n cursor: not-allowed;\n background-color: #eeeeee;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n color: #555;\n text-decoration: none;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-right: 15px;\n padding-left: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n margin-bottom: 0;\n border: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n filter: alpha(opacity=20);\n opacity: 0.2;\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n filter: alpha(opacity=50);\n opacity: 0.5;\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n filter: alpha(opacity=0);\n opacity: 0;\n}\n.modal-backdrop.in {\n filter: alpha(opacity=50);\n opacity: 0.5;\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: 400;\n line-height: 1.42857143;\n line-break: auto;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n white-space: normal;\n font-size: 12px;\n filter: alpha(opacity=0);\n opacity: 0;\n}\n.tooltip.in {\n filter: alpha(opacity=90);\n opacity: 0.9;\n}\n.tooltip.top {\n padding: 5px 0;\n margin-top: -3px;\n}\n.tooltip.right {\n padding: 0 5px;\n margin-left: 3px;\n}\n.tooltip.bottom {\n padding: 5px 0;\n margin-top: 3px;\n}\n.tooltip.left {\n padding: 0 5px;\n margin-left: -3px;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n right: 5px;\n bottom: 0;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: 400;\n line-height: 1.42857143;\n line-break: auto;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n white-space: normal;\n font-size: 14px;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow:after {\n content: \"\";\n border-width: 10px;\n}\n.popover.top > .arrow {\n bottom: -11px;\n left: 50%;\n margin-left: -11px;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n border-bottom-width: 0;\n}\n.popover.top > .arrow:after {\n bottom: 1px;\n margin-left: -10px;\n content: \" \";\n border-top-color: #fff;\n border-bottom-width: 0;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n border-left-width: 0;\n}\n.popover.right > .arrow:after {\n bottom: -10px;\n left: 1px;\n content: \" \";\n border-right-color: #fff;\n border-left-width: 0;\n}\n.popover.bottom > .arrow {\n top: -11px;\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n.popover.bottom > .arrow:after {\n top: 1px;\n margin-left: -10px;\n content: \" \";\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n right: 1px;\n bottom: -10px;\n content: \" \";\n border-right-width: 0;\n border-left-color: #fff;\n}\n.popover-title {\n padding: 8px 14px;\n margin: 0;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n.carousel-inner > .item {\n position: relative;\n display: none;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 15%;\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\n filter: alpha(opacity=50);\n opacity: 0.5;\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n background-repeat: repeat-x;\n}\n.carousel-control.right {\n right: 0;\n left: auto;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n background-repeat: repeat-x;\n}\n.carousel-control:hover,\n.carousel-control:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n filter: alpha(opacity=90);\n opacity: 0.9;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n z-index: 5;\n display: inline-block;\n margin-top: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n font-family: serif;\n line-height: 1;\n}\n.carousel-control .icon-prev:before {\n content: \"\\2039\";\n}\n.carousel-control .icon-next:before {\n content: \"\\203a\";\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n padding-left: 0;\n margin-left: -30%;\n text-align: center;\n list-style: none;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n border: 1px solid #fff;\n border-radius: 10px;\n}\n.carousel-indicators .active {\n width: 12px;\n height: 12px;\n margin: 0;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n right: 20%;\n left: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n display: table;\n content: \" \";\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-right: auto;\n margin-left: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","// stylelint-disable\n\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// 1. Remove the bottom border in Chrome 57- and Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n//\n\nabbr[title] {\n border-bottom: none; // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type\n\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n color: #000 !important; // Black prints faster: h5bp.com/s\n text-shadow: none !important;\n background: transparent !important;\n box-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n}\n","// stylelint-disable value-list-comma-newline-after, value-list-comma-space-after, indentation, declaration-colon-newline-after, font-family-no-missing-generic-family-keyword\n\n//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: \"Glyphicons Halflings\";\n src: url(\"@{icon-font-path}@{icon-font-name}.eot\");\n src: url(\"@{icon-font-path}@{icon-font-name}.eot?#iefix\") format(\"embedded-opentype\"),\n url(\"@{icon-font-path}@{icon-font-name}.woff2\") format(\"woff2\"),\n url(\"@{icon-font-path}@{icon-font-name}.woff\") format(\"woff\"),\n url(\"@{icon-font-path}@{icon-font-name}.ttf\") format(\"truetype\"),\n url(\"@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}\") format(\"svg\");\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: \"Glyphicons Halflings\";\n font-style: normal;\n font-weight: 400;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// https://getbootstrap.com/docs/3.4/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: https://a11yproject.com/posts/how-to-hide-content\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// stylelint-disable indentation, property-no-vendor-prefix, selector-no-vendor-prefix\n\n// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n word-wrap: break-word;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// stylelint-disable media-feature-name-no-vendor-prefix, media-feature-parentheses-space-inside, media-feature-name-no-unknown, indentation, at-rule-name-space-after\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","// stylelint-disable selector-list-comma-newline-after, selector-no-qualifying-type\n\n//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: 400;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n padding: .2em;\n background-color: @state-warning-bg;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-right: 5px;\n padding-left: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: 700;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n}\n\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: \"\\2014 \\00A0\"; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n text-align: right;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: \"\"; }\n &:after {\n content: \"\\00A0 \\2014\"; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n color: @pre-color;\n word-break: break-all;\n word-wrap: break-word;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n.row-no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n padding-right: ceil((@gutter / 2));\n padding-left: floor((@gutter / 2));\n margin-right: auto;\n margin-left: auto;\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-right: floor((@gutter / -2));\n margin-left: ceil((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-right: (@gutter / 2);\n padding-left: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-right: (@gutter / 2);\n padding-left: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-right: (@gutter / 2);\n padding-left: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-right: (@gutter / 2);\n padding-left: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-right: floor((@grid-gutter-width / 2));\n padding-left: ceil((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","// stylelint-disable selector-max-type, selector-max-compound-selectors, selector-no-qualifying-type\n\n//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n\n // Table cell sizing\n //\n // Reset default table behavior\n\n col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n display: table-column;\n float: none;\n }\n\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n display: table-cell;\n float: none;\n }\n }\n}\n\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\n\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n min-height: .01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n overflow-x: auto;\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * .75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","// stylelint-disable selector-no-qualifying-type, property-no-vendor-prefix, media-feature-name-no-vendor-prefix\n\n//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: 700;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\ninput[type=\"search\"] {\n // Override content-box in Normalize (* isn't specific enough)\n .box-sizing(border-box);\n\n // Search inputs in iOS\n //\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n -webkit-appearance: none;\n appearance: none;\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n\n // Apply same disabled cursor tweak as for inputs\n // Some special care is needed because