From cdef07ca7fde04a17444e6eef416511cd31a2259 Mon Sep 17 00:00:00 2001 From: Mihkel Kivisild Date: Wed, 27 Nov 2024 17:36:50 +0200 Subject: [PATCH] Merging web-eid-asp-dotnet-example repository into web-eid-authtoken-validation-dotnet repository WE2-933 Signed-off-by: Mihkel Kivisild --- .github/workflows/dotnet-build-example.yml | 55 ++ README.md | 3 + example/.gitignore | 367 ++++++++ example/LICENSE | 21 + example/README.md | 253 ++++++ example/src/.dockerignore | 27 + example/src/Dockerfile | 22 + example/src/WebEid.AspNetCore.Example.sln | 25 + .../Certificates/CertificateLoader.cs | 61 ++ .../Certificates/Dev/TEST_ORG_2021E.cer | Bin 0 -> 932 bytes .../Certificates/Dev/TEST_ORG_2021R.cer | Bin 0 -> 1744 bytes .../Certificates/Dev/TEST_of_ESTEID2018.cer | Bin 0 -> 1408 bytes .../Dev/TEST_of_KLASS3-SK_2016.cer | Bin 0 -> 1741 bytes .../Certificates/Prod/ESTEID2018.cer | Bin 0 -> 1371 bytes .../ClaimsIdentityExtensions.cs | 32 + .../Controllers/Api/AuthController.cs | 94 +++ .../Controllers/Api/BaseController.cs | 50 ++ .../Controllers/Api/ChallengeController.cs | 49 ++ .../Controllers/Api/SignController.cs | 75 ++ .../Controllers/WelcomeController.cs | 32 + .../DigiDoc/.gitkeep | 0 .../Dto/AuthenticateRequestDto.cs | 30 + .../Dto/CertificateDto.cs | 31 + .../Dto/ChallengeDto.cs | 26 + .../Dto/DigestDto.cs | 27 + .../WebEid.AspNetCore.Example/Dto/FileDto.cs | 31 + .../Dto/SignatureAlgorithmDto.cs | 28 + .../Dto/SignatureDto.cs | 31 + .../LoggedInAuthorizationHandler.cs | 40 + .../Pages/Index.cshtml | 117 +++ .../Pages/Welcome.cshtml | 131 +++ .../Pages/Welcome.cshtml.cs | 51 ++ .../Pages/_ViewStart.cshtml | 3 + .../src/WebEid.AspNetCore.Example/Program.cs | 39 + .../Properties/launchSettings.json | 36 + .../SessionBackedChallengeNonceStore.cs | 53 ++ .../Signing/DigiDocConfiguration.cs | 80 ++ .../Signing/SigningService.cs | 136 +++ .../src/WebEid.AspNetCore.Example/Startup.cs | 184 ++++ .../WebEid.AspNetCore.Example.csproj | 55 ++ .../appsettings.Development.json | 11 + .../appsettings.json | 11 + .../wwwroot/css/bootstrap.min.css | 7 + .../wwwroot/css/main.css | 69 ++ .../wwwroot/favicon.ico | Bin 0 -> 5430 bytes .../wwwroot/files/Web eID privacy policy.pdf | Bin 0 -> 76371 bytes .../wwwroot/files/example-for-signing.txt | 1 + .../wwwroot/img/eu-fund-flags.svg | 787 ++++++++++++++++++ .../wwwroot/js/errors.js | 59 ++ .../wwwroot/js/web-eid.js | 423 ++++++++++ example/src/docker-compose.yml | 8 + example/src/ria_public_key.gpg | Bin 0 -> 2215 bytes 52 files changed, 3671 insertions(+) create mode 100644 .github/workflows/dotnet-build-example.yml create mode 100644 example/.gitignore create mode 100644 example/LICENSE create mode 100644 example/README.md create mode 100644 example/src/.dockerignore create mode 100644 example/src/Dockerfile create mode 100644 example/src/WebEid.AspNetCore.Example.sln create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/CertificateLoader.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021E.cer create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021R.cer create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_ESTEID2018.cer create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_KLASS3-SK_2016.cer create mode 100644 example/src/WebEid.AspNetCore.Example/Certificates/Prod/ESTEID2018.cer create mode 100644 example/src/WebEid.AspNetCore.Example/ClaimsIdentityExtensions.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Controllers/Api/BaseController.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Controllers/Api/ChallengeController.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Controllers/Api/SignController.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Controllers/WelcomeController.cs create mode 100644 example/src/WebEid.AspNetCore.Example/DigiDoc/.gitkeep create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/AuthenticateRequestDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/CertificateDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/ChallengeDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/DigestDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/FileDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/SignatureAlgorithmDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Dto/SignatureDto.cs create mode 100644 example/src/WebEid.AspNetCore.Example/LoggedInAuthorizationHandler.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Pages/Index.cshtml create mode 100644 example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml create mode 100644 example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Pages/_ViewStart.cshtml create mode 100644 example/src/WebEid.AspNetCore.Example/Program.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Properties/launchSettings.json create mode 100644 example/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Signing/DigiDocConfiguration.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Signing/SigningService.cs create mode 100644 example/src/WebEid.AspNetCore.Example/Startup.cs create mode 100644 example/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj create mode 100644 example/src/WebEid.AspNetCore.Example/appsettings.Development.json create mode 100644 example/src/WebEid.AspNetCore.Example/appsettings.json create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/css/bootstrap.min.css create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/css/main.css create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/favicon.ico create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/files/Web eID privacy policy.pdf create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/files/example-for-signing.txt create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/img/eu-fund-flags.svg create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/js/errors.js create mode 100644 example/src/WebEid.AspNetCore.Example/wwwroot/js/web-eid.js create mode 100644 example/src/docker-compose.yml create mode 100644 example/src/ria_public_key.gpg diff --git a/.github/workflows/dotnet-build-example.yml b/.github/workflows/dotnet-build-example.yml new file mode 100644 index 0000000..d3e0a82 --- /dev/null +++ b/.github/workflows/dotnet-build-example.yml @@ -0,0 +1,55 @@ +name: Dotnet build example + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./example + + steps: + - uses: actions/checkout@v4 + + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x # SDK Version to use. + + - name: Cache Nuget packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + # Look to see if there is a cache hit for the corresponding requirements file + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + restore-keys: ${{ runner.os }}-nuget + + - name: Install dependencies + run: dotnet restore src/WebEid.AspNetCore.Example.sln --source "https://gitlab.com/api/v4/projects/35362906/packages/nuget/index.json" --source "https://api.nuget.org/v3/index.json" + + - name: Copy RIA repository key to keyrings + run: sudo cp src/ria_public_key.gpg /usr/share/keyrings/ria-repository.gpg + + - name: Add RIA repository to APT + run: | + echo "deb [signed-by=/usr/share/keyrings/ria-repository.gpg] https://installer.id.ee/media/ubuntu/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ria-repository.list + + - name: Update APT and install libdigidocpp-csharp + run: | + sudo apt update + sudo apt install -y --no-install-recommends libdigidocpp-csharp + + - name: Copy the necessary DigiDoc C# library files + run: sudo cp /usr/include/digidocpp_csharp/* src/WebEid.AspNetCore.Example/DigiDoc/ + + - name: Build + run: dotnet publish --configuration Release --no-restore src/WebEid.AspNetCore.Example.sln --verbosity normal + + - name: Test + run: dotnet test --no-restore --verbosity normal src/WebEid.AspNetCore.Example.sln + + - name: Test building Docker image + run: docker build -t web-eid-asp-dotnet-example . + working-directory: ./example/src diff --git a/README.md b/README.md index 513a5c4..58472ae 100644 --- a/README.md +++ b/README.md @@ -429,3 +429,6 @@ ChallengeNonce challengeNonce = nonceGenerator.GenerateAndStoreNonce(timeToLive) ``` The `GenerateAndStoreNonce(TimeSpan ttl)` method both generates the nonce and stores it in the store. The `ttl` parameter defines nonce time-to-live duration. When the time-to-live passes, the nonce is considered to be expired. + +# Web eID ASP.NET example +See the [example documentation](example/README.md). \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..613f6dd --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,367 @@ +# Web eID specific +*.swp +/src/WebEid.AspNetCore.Example/DigiDoc/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +/TestsResults diff --git a/example/LICENSE b/example/LICENSE new file mode 100644 index 0000000..29b2598 --- /dev/null +++ b/example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-2024 Estonian Information System Authority + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..7398844 --- /dev/null +++ b/example/README.md @@ -0,0 +1,253 @@ +# Web eID ASP.NET example + +![European Regional Development Fund](https://github.com/open-eid/DigiDoc4-Client/blob/master/client/images/EL_Regionaalarengu_Fond.png) + +This project is an example ASP.NET web application that shows how to implement strong authentication and digital signing with electronic ID smart cards using Web eID. + +More information about the Web eID project is available on the project [website](https://web-eid.eu/). + +The ASP.NET web application makes use of the following technologies: + +- ASP.NET MVC, +- the Web eID authentication token validation library [_web-eid-authtoken-validation-dotnet_](../README.md), +- the Web eID JavaScript library [_web-eid.js_](https://github.com/web-eid/web-eid.js), +- the digital signing library [_libdigidocpp_](https://github.com/open-eid/libdigidocpp/tree/master/examples/DigiDocCSharp). + +Note that for including the Web eID authentication token validation library as a nuget package you need to have added a Package Source with the following address to the NuGet Package Manager: +https://gitlab.com/api/v4/projects/35362906/packages/nuget/index.json + +## Quickstart + +Complete the steps below to run the example application in order to test authentication and digital signing with Web eID. + +### 1. Configure the origin URL + +One crucial step of the Web eID authentication token validation algorithm is verifying the token signature. The value that is signed contains the site origin URL (the URL serving the web application) to protect against man-in-the-middle attacks. Hence the site origin URL must be configured in application settings. + +To configure the origin URL, add `OriginUrl` field in the application settings file `appsettings.json` as follows: +```json +{ + "OriginUrl": "https://example.org" +} +``` +Note that the URL **must not end with a slash** `/`. + +### 2. Configure the trusted certificate authority certificates + +The algorithm, which performs the validation of the Web eID authentication token, needs to know which intermediate certificate authorities (CA) are trusted to issue the eID authentication certificates. CA certificates are loaded from `.cer` files in the profile-specific subdirectory of the [`Certificates` resource directory](https://github.com/web-eid/web-eid-asp-dotnet-example/src/WebEid.AspNetCore.Example/Certificates). By default, Estonian eID test CA certificates are included in the `Development` profile and production CA certificates in the `Production` profile. + +In case you need to provide your own CA certificates, add the `.cer` files to the `src/WebEid.AspNetCore.Example/Certificates/{Dev,Prod}` profile-specific directory. + +### 3. Setup the `libdigidocpp` library for signing + +`libdigidocpp` is a library for creating, signing and verifying digitally signed documents according to XAdES and XML-DSIG standards. It is a C++ library that has [SWIG](http://swig.org/) bindings for C#. + +Set up the `libdigidocpp` library as follows: + +#### For MS Windows + +1. Install the _libdigidocpp-3.17.1.msi_ package or higher. The installation packages are available from [https://github.com/open-eid/libdigidocpp/releases](https://github.com/open-eid/libdigidocpp/releases). +2. Copy the C# source files from the `libdigidocpp` installation folder `include\digidocpp_csharp` to the `src\WebEid.AspNetCore.Example\DigiDoc` folder. +3. Copy all files from either the `x64` subfolder of the `libdigidocpp` installation folder to the example application build output folder `bin\...\net8.0` (after building, see next step). When building custom applications, choose `x64` if your application is 64-bit and `x86` if it is 32-bit. +4. When running in the `Development` profile, create an empty file named `EE_T.xml` for TSL cache as described in the [_Using test TSL lists_](https://github.com/open-eid/libdigidocpp/wiki/Using-test-TSL-lists#preconditions) section of the `libdigidocpp` wiki. + +#### For Ubuntu Linux + +1. Add RIA repository to install the official _libdigidocpp-csharp_ package: + ```sh + wget https://github.com/web-eid/web-eid-asp-dotnet-example/raw/main/src/ria_public_key.gpg + cp ria_public_key.gpg /usr/share/keyrings/ria-repository.gpg + echo "deb [signed-by=/usr/share/keyrings/ria-repository.gpg] https://installer.id.ee/media/ubuntu/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/ria-repository.list + ``` +2. Install the _libdigidocpp-csharp_ package: + ```sh + apt update + apt install -y --no-install-recommends libdigidocpp-csharp + ``` +3. Navigate to the `src` directory: + + ```sh + cd src + ``` +4. Copy the necessary DigiDoc C# library files into your project: + + ```sh + cp /usr/include/digidocpp_csharp/* WebEid.AspNetCore.Example/DigiDoc/ + ``` + +#### For macOS + +1. Install the _libdigidocpp-3.17.1.pkg_ package or higher. The installation packages are available from [https://github.com/open-eid/libdigidocpp/releases](https://github.com/open-eid/libdigidocpp/releases). +2. Copy the C# source files from `/Library/libdigidocpp/include/digidocpp_csharp` directory to `src/WebEid.AspNetCore.Example/DigiDoc` directory. +3. Go to `src/WebEid.AspNetCore.Example/bin/.../net8.0` directory and create symbolic link to `/Library/libdigidocpp/lib/libdigidoc_csharp.dylib` library: + ```cmd + ln -s /Library/libdigidocpp/lib/libdigidoc_csharp.dylib + ``` + +Further information is available in the [libdigidocpp example C# application source code](https://github.com/open-eid/libdigidocpp/tree/master/examples/DigiDocCSharp) and in the [`libdigidocpp` Wiki](https://github.com/open-eid/libdigidocpp/wiki). + +### 4. Build the application + +You need to have the [.NET 6.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed for building the application package. +Build the application by running the following command in a terminal window under the `src` directory: + +```cmd +dotnet build +``` + +### 5. Choose either the `Development` or `Production` profile + +If you have a test eID card, use the `Development` profile. In this case access to paid services is not required, but you need to upload the authentication and signing certificates of the test card to the test OCSP responder database as described in section _[Using DigiDoc4j in test mode with the `dev` profile](https://github.com/web-eid/web-eid-spring-boot-example#using-digidoc4j-in-test-mode-with-the-dev-profile)_ of the Web eID Java example application documentation. The`Development` profile is activated by default. + +If you only have a production eID card, i.e. an eID card issued to a real person or organization, use the `Production` profile. You can still test authentication without further configuration; however, for digital signing to work, you need access to a paid timestamping service as described in section [_Using DigiDoc4j in production mode with the `prod` profile_](https://github.com/web-eid/web-eid-spring-boot-example#using-digidoc4j-in-production-mode-with-the-prod-profile) of the Web eID Java example documentation. + +You can specify the profile as an environment variable `ASPNETCORE_ENVIRONMENT` when running the application. To set the profile for the current session before starting the app using [`dotnet run`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-run), use the following command: +```cmd +set ASPNETCORE_ENVIRONMENT=Production +``` + +### 6. Run the application + +Run the application with the following command in a terminal window under the `src` directory: + +```cmd +dotnet run --project WebEid.AspNetCore.Example +``` + +This will activate the default `Development` profile and launch the built-in `kestrel` web server on HTTPS port 5001. + +When the application has started, open https://localhost:5001 in your preferred web browser and follow instructions on the front page. + +## Overview of the source code + +The `src\WebEid.AspNetCore.Example` directory contains the ASP.NET application source code and resources. The subdirectories therein have the following purpose: +- `wwwroot`: web server static content, including CSS and JavaScript files, +- `Certificates`: CA certificates in profile-specific subdirectories, +- `Controllers`: ASP.NET MVC controller for the welcome page and Web API controllers that provide endpoints for + - getting the challenge nonce used by the authentication token validation library, + - logging in, + - digital signing, +- `DigiDoc`: contains the C# binding files of the `libdigidocpp` library; these files must be copied from the `libdigidocpp` installation directory `\include\digidocpp_csharp`, +- `Pages`: Razor pages, +- `Services`: Web eID signing service implementation that uses `libdigidocpp`. + +## More information + +See the [Web eID Java example application documentation](https://github.com/web-eid/web-eid-spring-boot-example) for more information, including answers to questions not answered below. + +### Running the application with local `WebEid.Security` library +To use the local version of the `WebEid.Security` library, you have to modify the `src\WebEid.AspNetCore.Example\WebEid.AspNetCore.Example.csproj` file as follows: + 1. Build the `WebEid.Security` solution + 2. Comment out or remove the following line: + ```xml + + ``` + 3. Add a new section to the `.csproj` file under the `ItemGroup` with package references: + ```xml + + + ..\..\..\src\WebEid.Security\bin\Release\netstandard2.1\WebEid.Security.dll + + + ``` + +### Frequently asked questions + +#### Why do I get the `System.ApplicationException: Failed to verify OCSP Responder certificate` error during signing? + +You are running in the `Development` profile, but you have not created an empty file named `EE_T.xml` for TSL cache. Creating the file is mandatory and is described in more detail in the [_Using test TSL lists_](https://github.com/open-eid/libdigidocpp/wiki/Using-test-TSL-lists#preconditions) section of the `libdigidocpp` wiki. + +#### Why do I get the `System.BadImageFormatException: An attempt was made to load a program with an incorrect format` error during signing? + +You are using `libdigidocpp` DLLs for the wrong architecture. Copy files from the `x64` subfolder of the `libdigidocpp` installation folder to right place as described in the section _3. Setup the `libdigidocpp` library for signing_ above. In case you get this error while developing a custom 32-bit application, copy files from the `x86` subfolder instead. + +## Building and running with Docker on Ubuntu Linux + +This section covers the steps required to build the application on an Ubuntu Linux environment and run it using Docker. + +### Prerequisites + +Before you begin, ensure you have the following installed on your system: + +- .NET SDK 7.0 +- libdigidocpp-csharp + +You can install them using the following command: + +```sh +sudo apt install dotnet-sdk-7.0 libdigidocpp-csharp +``` + +Note: Before installing `libdigidocpp-csharp` you have to have added the RIA repository as a package source. See [For Ubuntu Linux section](#for-ubuntu-linux) for information. + +### Building the application + +To build the application, follow these steps: + +1. Navigate to the `src` directory: + + ```sh + cd src + ``` + +2. Copy the necessary DigiDoc C# library files into your project: + + ```sh + cp /usr/include/digidocpp_csharp/* WebEid.AspNetCore.Example/DigiDoc/ + ``` + +3. Publish the application with the Release configuration: + + ```sh + dotnet publish --configuration Release WebEid.AspNetCore.Example.sln + ``` + +4. Update the `OriginUrl` in the `appsettings.json` to match your production environment: + + ```sh + sed -i 's#"OriginUrl": "https://localhost:44391"#"OriginUrl": "https://example.com"#' WebEid.AspNetCore.Example/bin/Release/net6.0/publish/appsettings.json + ``` + +### Building the Docker image + +After successfully building the application, you can create a Docker image: + +```sh +docker build -t web-eid-asp-dotnet-example . +``` + +This command builds a Docker image named `web-eid-asp-dotnet-example` using the `Dockerfile` in the current directory. + +## Running the Docker container with HTTPS support + +To enable HTTPS support for the .NET application, you have two primary options: + +1. Directly configure Kestrel to use HTTPS by setting up the necessary certificate information in the app's configuration files. This method is detailed in the [ASP.NET Core documentation](https://docs.microsoft.com/aspnet/core/security/enforcing-ssl). + +2. Employ a reverse proxy that manages TLS termination and forwards requests to the application over HTTP. This is a common pattern in production environments due to its flexibility. + +In this project, we assume the application is running behind a reverse proxy. + +First, the proxy server must pass the `Host:` line from the incoming request to the proxied application and set the `X-Forwarded-*` headers to inform the application that it runs behind a reverse proxy. Here is example configuration for the Apache web server: + + + ProxyPreserveHost On + ProxyPass http://localhost:8480/ + ProxyPassReverse http://localhost:8480/ + RequestHeader set X-Forwarded-Proto https + RequestHeader set X-Forwarded-Port 443 + + + +Next, the .NET application must be configured to recognize and honor the `X-Forwarded-*` headers. This can be done by configuring the Forwarded Headers middleware in `Startup.cs`: + +```csharp +app.UseForwardedHeaders(new ForwardedHeadersOptions +{ + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto +}); +``` + +By default, this middleware is already enabled in the application. + +A Docker Compose configuration file `docker-compose.yml` is available in the `src` directory for running the Docker image `web-eid-asp-dotnet-example` on port 8480 behind a reverse proxy. diff --git a/example/src/.dockerignore b/example/src/.dockerignore new file mode 100644 index 0000000..50e2413 --- /dev/null +++ b/example/src/.dockerignore @@ -0,0 +1,27 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md + +!WebEid.AspNetCore.Example/bin/Release/net8.0/publish/ diff --git a/example/src/Dockerfile b/example/src/Dockerfile new file mode 100644 index 0000000..33b8ad1 --- /dev/null +++ b/example/src/Dockerfile @@ -0,0 +1,22 @@ +# In the future, we should use chiseled images. +FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy + +WORKDIR /app + +COPY ria_public_key.gpg /usr/share/keyrings/ria-repository.gpg + +# Add RIA repository to install the official libdigidocpp-csharp package. As each RUN commits the layer to image, +# need to chain commands and clean up in the end to keep the image small. +RUN echo "deb [signed-by=/usr/share/keyrings/ria-repository.gpg] https://installer.id.ee/media/ubuntu/ jammy main" > /etc/apt/sources.list.d/ria-repository.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends libdigidocpp-csharp && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists + +COPY ./WebEid.AspNetCore.Example/bin/Release/net8.0/publish/ . + +ENV ASPNETCORE_ENVIRONMENT=Production + +EXPOSE 80 + +ENTRYPOINT ["dotnet", "WebEid.AspNetCore.Example.dll"] diff --git a/example/src/WebEid.AspNetCore.Example.sln b/example/src/WebEid.AspNetCore.Example.sln new file mode 100644 index 0000000..72f5596 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebEid.AspNetCore.Example", "WebEid.AspNetCore.Example\WebEid.AspNetCore.Example.csproj", "{573AD725-C52C-40DE-8E70-6DF4E4227120}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|Any CPU.Build.0 = Debug|Any CPU + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|Any CPU.ActiveCfg = Release|Any CPU + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {551C3D4A-5A1B-427B-8753-923D6BD1A44B} + EndGlobalSection +EndGlobal diff --git a/example/src/WebEid.AspNetCore.Example/Certificates/CertificateLoader.cs b/example/src/WebEid.AspNetCore.Example/Certificates/CertificateLoader.cs new file mode 100644 index 0000000..186694c --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Certificates/CertificateLoader.cs @@ -0,0 +1,61 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Certificates +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Security.Cryptography.X509Certificates; + + internal static class CertificateLoader + { + public static X509Certificate2[] LoadTrustedCaCertificatesFromDisk(bool isTest = false) + { + return new FileReader(GetCertPath(isTest), "*.cer").ReadFiles() + .Select(file => new X509Certificate2(file)) + .ToArray(); + } + + private static string GetCertPath(bool isTest) + { + return isTest ? "Certificates/Dev" : "Certificates/Prod"; + } + } + + internal class FileReader + { + private readonly string path; + private readonly string searchPattern; + + public FileReader(string path, string searchPattern = null) + { + this.path = path; + this.searchPattern = searchPattern; + } + + public IEnumerable ReadFiles() + { + foreach (var file in Directory.EnumerateFiles(this.path, this.searchPattern)) + { + yield return File.ReadAllBytes(file); + } + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021E.cer b/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021E.cer new file mode 100644 index 0000000000000000000000000000000000000000..6246e0ce7ebf75ca55cc998c943558fe7aa19f33 GIT binary patch literal 932 zcmXqLVqRd-#LT>anTe5!NuXG3+KOpOU}k=R`7ID2+q$bEy>K!D^_p}HWW7y1xY6I@cD%Vxw`5a z8kn1y8yFfJY8t46B$#;=LR^DG6!Oy)&~*p-`-dpF8@d|EiSrr(^%)r%SeTd?nnj88 z8k-?;4f3hs9xVe6hD2$*e?3O()Q!mx&K6(3!<8b-!?@$Hj0?;6qh=*RHzwx(vYMfp zzjb<%;FKg zcmsK$OJ$W=Bn-qFL>|f9OTM%3M@t=}wVrpw#)tJ+KRz>%1u5WT5n~bYTrP7Vda8u6 z=s~$pGv+<7_U3ELG~feC^D{F3XJG+GH=BVFh%XG{a~QAzDJDh+gB&&vZ8k<$R(3{4 zmIQ-110@*WfU!+1qokz3N?$)EH8)?cI9o3@RX;zuxWK>yu7;^i2dX9+ssI$K@xk8l zpjeG}H;e~|q+UvDkzR69iGd}^1@bIL1_lOt3v?D}wP_HgJ-H~yAONIQfyL9n-N2QN z1;}JDur;uTxrB*POdn)aG0?f?<>f%fWTq6u5~yBkseWpneo<;cesN|=eo>{qb3m{@ za$09@>|!uzY-4ha)8k?)PhOyL{%^wBdEGY5hS&b^G@51BM?F1rEwE;#=lc1~6R)q= z(_o02arb$(Z`Om%`iTv8a(xdEt2&q0M$SIZ-7NspCGyb literal 0 HcmV?d00001 diff --git a/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021R.cer b/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_ORG_2021R.cer new file mode 100644 index 0000000000000000000000000000000000000000..f2749c79c3e873ef1ff7dda93100fa1c8e39027c GIT binary patch literal 1744 zcmb`HdoM)q+hLpwW6AuN>?v}(mIQ8B;fuZJVs#cUpy=SjmOoU zNi+qfQXnB(@7q6tuqccWt&Ec1E1-pF2-QR&*v@|EdeAd_3%OH3d*i>t+9>x!LrJw& zTUG$);C&o(nARzOs@2n`>ZLrtg3>s0I$u5Co7B7k6O@(}I#qaPgm4CpcIrBR4d3FX zSGKTsXYQ`lxJmEz4+ofU8hWOWI=F~u-uDxF^UBpz!(JkbKAwp6JCZ@$C2zuq*VEKk!Ts*EX#ha@>R?Kbl- z_aqOO6nrxAjCo(Gw&v5}v+fDy+W0h*t$}k2s>%sdKe(prLZJ3*w@Ge6*x5bE?S)6> z^Bp=iWtS7|je0L>Zi&?y(5Osp7uC&_c3wPYQM@Vra}B9N&NwnB|F-RXcWm6?3w_79 z6z4;=I<2oW>jHU|=UjSL_7n6YR7UFFK5~kTkYnKu?(eAV7ZtRBZ(DkU^J>AcN~fA| z4mLL2-RkOW@lJj6o>G#5rxlc19(|j8G;sISn5w4 zP?c#lnU)7Q=_YMB5QdW{;UFwl4uW7m-~;r&Y5*lJ+ba-6+E6F~oNz9gA4=wMC|ou_ z5?KA6gHAO5lEeOzAoGAP;^Zsypf8i^`)A*i12{Y~nGEO z*}O2YO;$>T9RU+CaIo}<0=9tml>`kfqsX4(OL3#4qopvx0sODygv^Pea3Ux?P9&Ef zEa38DC=S~Y%C|<84kxJ*AyiUPJnpA$hLd_Tm)_|dh>!Y?-Zy7~ zSl%2LhaQ})QttPrBtG^Pd@>Rq0S zzehK+2=5dh3ek1SyH$U`?vit)g7>4hrhALd?&z7$yf}D*=fP`Tvh-klaiy*A>(-}l z2dlX`H%w>_Tg@`oa)?a>HPfY^ob?SdPEOo8Ud49F%(3ifMsD}*J$y5nkIlM!&P_6h z3Xc^OPnZLN{{W^FV6e41m@bz zzB@FvtHAWvJ3Sm@kuo>vfZ9waIM|hj-L#k&TQ+8!At4za4T-aEd`wb1U8nI@klvtS z6DnUs62rX3?X1;NT|31pCCZrfVRWmH8(@a{Q0aTp?#+^J#=Xib&dj`7ac#malk+cn zz1j8Y>D}u2+jCk~;G@pk-1QuBgZtgtd{I|b#PV0yHkZ#G_&8X+`(n!tRU!xWOYe}t z!UZjbq9>@#ko;5gh}lQ~XXZMHZkuscv5EO(gNMU3kL&rZ>p#_Cj-tJ1Mewcx$#@V=dx*q*Tq87_$>*8{E|5xceC9MAGI%bs)iK{LH; P?4i$f_J{Ib%30ALokzCI literal 0 HcmV?d00001 diff --git a/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_ESTEID2018.cer b/example/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_ESTEID2018.cer new file mode 100644 index 0000000000000000000000000000000000000000..6a96fb083686e6210a1c5f14cfd0cf7780227843 GIT binary patch literal 1408 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 literal 0 HcmV?d00001 diff --git a/example/src/WebEid.AspNetCore.Example/ClaimsIdentityExtensions.cs b/example/src/WebEid.AspNetCore.Example/ClaimsIdentityExtensions.cs new file mode 100644 index 0000000..ddc8983 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/ClaimsIdentityExtensions.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example +{ + using System.Linq; + using System.Security.Claims; + + public static class ClaimsIdentityExtensions + { + public static string GetIdCode(this ClaimsIdentity identity) + { + return identity.Claims.SingleOrDefault(claim => claim.Type == ClaimTypes.NameIdentifier)?.Value; + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs b/example/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs new file mode 100644 index 0000000..b8873fd --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Controllers.Api +{ + using Microsoft.AspNetCore.Authentication; + using Microsoft.AspNetCore.Authentication.Cookies; + using Microsoft.AspNetCore.Mvc; + using Security.Util; + using Security.Validator; + using System.Collections.Generic; + using System.Security.Claims; + using System.Threading.Tasks; + using Security.Challenge; + using WebEid.AspNetCore.Example.Dto; + using System; + + [Route("[controller]")] + [ApiController] + public class AuthController : BaseController + { + private readonly IAuthTokenValidator authTokenValidator; + private readonly IChallengeNonceStore challengeNonceStore; + + public AuthController(IAuthTokenValidator authTokenValidator, IChallengeNonceStore challengeNonceStore) + { + this.authTokenValidator = authTokenValidator; + this.challengeNonceStore = challengeNonceStore; + } + + [HttpPost] + [Route("login")] + public async Task Login([FromBody] AuthenticateRequestDto authToken) + { + var certificate = await this.authTokenValidator.Validate(authToken.AuthToken, this.challengeNonceStore.GetAndRemove().Base64EncodedNonce); + + List claims = new(); + + AddNewClaimIfCertificateHasData(claims, ClaimTypes.GivenName, certificate.GetSubjectGivenName); + AddNewClaimIfCertificateHasData(claims, ClaimTypes.Surname, certificate.GetSubjectSurname); + AddNewClaimIfCertificateHasData(claims, ClaimTypes.NameIdentifier, certificate.GetSubjectIdCode); + AddNewClaimIfCertificateHasData(claims, ClaimTypes.Name, certificate.GetSubjectCn); + + var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + + var authProperties = new AuthenticationProperties + { + AllowRefresh = true + }; + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), + authProperties); + + // Assign a unique ID within the session to enable the use of a unique temporary container name across successive requests. + // A unique temporary container name is required to facilitate simultaneous signing from multiple browsers. + SetUniqueIdInSession(); + } + + [HttpGet] + [Route("logout")] + public async Task Logout() + { + RemoveUserContainerFile(); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + } + + private static void AddNewClaimIfCertificateHasData(List claims, string claimType, Func dataGetter) + { + var claimData = dataGetter(); + if (!string.IsNullOrEmpty(claimData)) + { + claims.Add(new Claim(claimType, claimData)); + } + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Controllers/Api/BaseController.cs b/example/src/WebEid.AspNetCore.Example/Controllers/Api/BaseController.cs new file mode 100644 index 0000000..79831e3 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Controllers/Api/BaseController.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Controllers.Api +{ + using System; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + + public abstract class BaseController : ControllerBase + { + const string uniqueIdKey = "UniqueId"; + + protected void RemoveUserContainerFile() + { + System.IO.File.Delete(GetUserContainerName()); + } + + protected void SetUniqueIdInSession() + { + HttpContext.Session.SetString(uniqueIdKey, Guid.NewGuid().ToString()); + } + + private string GetUniqueIdFromSession() + { + return HttpContext.Session.GetString(uniqueIdKey); + } + + protected string GetUserContainerName() + { + return $"container_{GetUniqueIdFromSession()}"; + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Controllers/Api/ChallengeController.cs b/example/src/WebEid.AspNetCore.Example/Controllers/Api/ChallengeController.cs new file mode 100644 index 0000000..764a1bf --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Controllers/Api/ChallengeController.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Controllers.Api +{ + using Microsoft.AspNetCore.Mvc; + using Security.Challenge; + using System; + using WebEid.AspNetCore.Example.Dto; + + [Route("auth")] + [ApiController] + public class ChallengeController : BaseController + { + private readonly IChallengeNonceGenerator challengeNonceGenerator; + + public ChallengeController(IChallengeNonceGenerator challengeNonceGenerator) + { + this.challengeNonceGenerator = challengeNonceGenerator; + } + + [HttpGet] + [Route("challenge")] + public ChallengeDto GetChallenge() + { + var challenge = new ChallengeDto + { + Nonce = challengeNonceGenerator.GenerateAndStoreNonce(TimeSpan.FromMinutes(5)).Base64EncodedNonce + }; + return challenge; + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Controllers/Api/SignController.cs b/example/src/WebEid.AspNetCore.Example/Controllers/Api/SignController.cs new file mode 100644 index 0000000..d4f6806 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Controllers/Api/SignController.cs @@ -0,0 +1,75 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Controllers.Api +{ + using System; + using System.Security.Claims; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Logging; + using Services; + using WebEid.AspNetCore.Example.Dto; + + [Route("[controller]")] + [ApiController] + public class SignController : BaseController + { + private const string SignedFile = "example-for-signing.asice"; + private readonly SigningService signingService; + private readonly ILogger logger; + + public SignController(SigningService signingService, ILogger logger) + { + this.signingService = signingService; + this.logger = logger; + } + + [Route("prepare")] + [HttpPost] + public DigestDto Prepare([FromBody] CertificateDto data) + { + return signingService.PrepareContainer(data, (ClaimsIdentity)HttpContext.User.Identity, GetUserContainerName()); + } + + [Route("sign")] + [HttpPost] + public FileDto Sign([FromBody] SignatureDto data) + { + signingService.SignContainer(data, GetUserContainerName()); + return new FileDto(SignedFile); + } + + [Route("download")] + [HttpGet] + public async Task Download() + { + try + { + var content = await System.IO.File.ReadAllBytesAsync(GetUserContainerName()); + return File(content, "application/vnd.etsi.asic-e+zip", SignedFile); + } + catch (Exception ex) + { + logger?.LogError(ex, "Error occurred while downloading user container file"); + return BadRequest(); + } + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Controllers/WelcomeController.cs b/example/src/WebEid.AspNetCore.Example/Controllers/WelcomeController.cs new file mode 100644 index 0000000..c9743e4 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Controllers/WelcomeController.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Controllers +{ + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + + public class WelcomeController : Controller + { + public IActionResult Index() + { + return View(); + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/DigiDoc/.gitkeep b/example/src/WebEid.AspNetCore.Example/DigiDoc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/example/src/WebEid.AspNetCore.Example/Dto/AuthenticateRequestDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/AuthenticateRequestDto.cs new file mode 100644 index 0000000..c36726f --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/AuthenticateRequestDto.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + using System.Text.Json.Serialization; + using Security.AuthToken; + + public class AuthenticateRequestDto + { + [JsonPropertyName("auth-token")] + public WebEidAuthToken AuthToken { get; set; } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Dto/CertificateDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/CertificateDto.cs new file mode 100644 index 0000000..5fabd62 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/CertificateDto.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + using System.Collections.Generic; + using System.Text.Json.Serialization; + + public class CertificateDto + { + [JsonPropertyName("certificate")] + public string CertificateBase64String { get; set; } + public IList SupportedSignatureAlgorithms { get; set; } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Dto/ChallengeDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/ChallengeDto.cs new file mode 100644 index 0000000..63aa24c --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/ChallengeDto.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + public class ChallengeDto + { + public string Nonce { get; set; } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Dto/DigestDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/DigestDto.cs new file mode 100644 index 0000000..08b1d87 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/DigestDto.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + public class DigestDto + { + public string Hash { get; set; } + public string HashFunction { get; set; } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Dto/FileDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/FileDto.cs new file mode 100644 index 0000000..b29cd14 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/FileDto.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + public class FileDto + { + public FileDto(string name) + { + this.Name = name; + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Dto/SignatureAlgorithmDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/SignatureAlgorithmDto.cs new file mode 100644 index 0000000..3ac7527 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/SignatureAlgorithmDto.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + public class SignatureAlgorithmDto + { + public string CryptoAlgorithm { get; set; } + public string HashFunction { get; set; } + public string PaddingScheme { get; set; } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Dto/SignatureDto.cs b/example/src/WebEid.AspNetCore.Example/Dto/SignatureDto.cs new file mode 100644 index 0000000..ff5e9d8 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Dto/SignatureDto.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Dto +{ + public class SignatureDto + { + public SignatureAlgorithmDto SignatureAlgorithm { get; set; } + + /// + /// Base64 signature + /// + public string Signature { get; set; } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/LoggedInAuthorizationHandler.cs b/example/src/WebEid.AspNetCore.Example/LoggedInAuthorizationHandler.cs new file mode 100644 index 0000000..dc01f3e --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/LoggedInAuthorizationHandler.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example +{ + using Microsoft.AspNetCore.Authorization; + using System.Threading.Tasks; + + public class LoggedInAuthorizationHandler : AuthorizationHandler + { + protected override Task HandleRequirementAsync( + AuthorizationHandlerContext context, LoggedInRequirement requirement) + { + if (context.User.Identity.IsAuthenticated) + { + context.Succeed(requirement); + } + return Task.CompletedTask; + } + } + + public class LoggedInRequirement : IAuthorizationRequirement { } + +} diff --git a/example/src/WebEid.AspNetCore.Example/Pages/Index.cshtml b/example/src/WebEid.AspNetCore.Example/Pages/Index.cshtml new file mode 100644 index 0000000..d25e828 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Pages/Index.cshtml @@ -0,0 +1,117 @@ +@page +@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf + + + + + + + Web eID: electronic ID smart cards on the Web + + + + +
+
+
+

Web eID: electronic ID smart cards on the Web

+

+ The Web eID project enables usage of European Union electronic identity (eID) smart cards for + secure authentication and digital signing of documents on the web using public-key cryptography. +

+

+ Estonian, Finnish, Latvian, Lithuanian and Croatian eID cards are supported in the first phase, but only + Estonian eID card support is currently enabled in the test application below. +

+

+ Please get in touch by email at help@ria.ee in case you need support with adding Web eID to your project + or want to add support for a new eID card to Web eID. +

+ +
+ +

+ More information about the Web eID project, including installation and usage instructions + is available on the project [website](https://web-eid.eu/). +

+

Click Authenticate below to test authentication and digital signing.

+ + +

+ +

+ +

+ The privacy policy of the test service is available here. +

+
+
+
+ +
+ EU fund flags +
+ + + + diff --git a/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml b/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml new file mode 100644 index 0000000..3f7eaad --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml @@ -0,0 +1,131 @@ +@page +@model WebEid.AspNetCore.Example.Pages.WelcomeModel +@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf + + + + + + + Welcome! + + + + +
+
+
+
+

Digital signing

+

Welcome, @Model.PrincipalName!

+
+ +
+ +
+

+ To test digital signing, you can sign the following document by clicking + Sign document below: +

+ +
+ + +

+ + +

+
+ +
+
+ + + + diff --git a/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs b/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs new file mode 100644 index 0000000..cc1216f --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Pages +{ + using System.Linq; + using System.Security.Claims; + using Microsoft.AspNetCore.Mvc.RazorPages; + + public class WelcomeModel : PageModel + { + public string PrincipalName => GetPrincipalName((ClaimsIdentity)this.User.Identity); + + private static string GetPrincipalName(ClaimsIdentity identity) + { + var givenName = identity.Claims.Where(claim => claim.Type == ClaimTypes.GivenName) + .Select(claim => claim.Value) + .SingleOrDefault(); + var surname = identity.Claims.Where(claim => claim.Type == ClaimTypes.Surname) + .Select(claim => claim.Value) + .SingleOrDefault(); + + if (!string.IsNullOrEmpty(givenName) && !string.IsNullOrEmpty(surname)) + { + return $"{givenName} {surname}"; + } + else + { + return identity.Claims.Where(claim => claim.Type == ClaimTypes.Name) + .Select(claim => claim.Value) + .SingleOrDefault(); + } + } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Pages/_ViewStart.cshtml b/example/src/WebEid.AspNetCore.Example/Pages/_ViewStart.cshtml new file mode 100644 index 0000000..be2a4f3 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Pages/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = null; +} diff --git a/example/src/WebEid.AspNetCore.Example/Program.cs b/example/src/WebEid.AspNetCore.Example/Program.cs new file mode 100644 index 0000000..af5995f --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Program.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Hosting; + + public static class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Properties/launchSettings.json b/example/src/WebEid.AspNetCore.Example/Properties/launchSettings.json new file mode 100644 index 0000000..1532817 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Properties/launchSettings.json @@ -0,0 +1,36 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:60500", + "sslPort": 44391 + } + }, + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:44391", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "publishAllPorts": true, + "useSSL": true + } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs b/example/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs new file mode 100644 index 0000000..e269f36 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using Microsoft.AspNetCore.Http; +using System.Text.Json; +using WebEid.Security.Challenge; + +namespace WebEid.AspNetCore.Example +{ + public class SessionBackedChallengeNonceStore : IChallengeNonceStore + { + private const string ChallengeNonceKey = "challenge-nonce"; + private readonly IHttpContextAccessor httpContextAccessor; + + public SessionBackedChallengeNonceStore(IHttpContextAccessor httpContextAccessor) + { + this.httpContextAccessor = httpContextAccessor; + } + + public void Put(ChallengeNonce challengeNonce) + { + this.httpContextAccessor.HttpContext.Session.SetString(ChallengeNonceKey, JsonSerializer.Serialize(challengeNonce)); + } + + public ChallengeNonce GetAndRemoveImpl() + { + var httpContext = this.httpContextAccessor.HttpContext; + var challenceNonceJson = httpContext.Session.GetString(ChallengeNonceKey); + if (!string.IsNullOrWhiteSpace(challenceNonceJson)) + { + httpContext.Session.Remove(ChallengeNonceKey); + return JsonSerializer.Deserialize(challenceNonceJson); + } + return null; + } + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/Signing/DigiDocConfiguration.cs b/example/src/WebEid.AspNetCore.Example/Signing/DigiDocConfiguration.cs new file mode 100644 index 0000000..6e4854c --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Signing/DigiDocConfiguration.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Services +{ + using digidoc; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Hosting; + using System; + + public class DigiDocConfiguration + { + /// + /// Base64 cert from https://open-eid.github.io/test-TL/trusted-test-tsl.crt + /// + private const string TestTslCert = @"MIIEvDCCAqQCCQCL/COUVyiGjTANBgkqhkiG9w0BAQUFADAgMQswCQYDVQQGEwJF +RTERMA8GA1UEAwwIVGVzdCBUU0wwHhcNMTgxMTE1MTI1MjU1WhcNMjgxMTEyMTI1 +MjU1WjAgMQswCQYDVQQGEwJFRTERMA8GA1UEAwwIVGVzdCBUU0wwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDfFK0fYeGrdngMZXZndDEpcl9pjGGNpbie +3+ch5mDqObUe+OL45b4+SfPapriVRNBa+m5T1TuijP7Kb8sTNS9U3WQYvY8bEstP +ZnaEvdQSSVRf4j9eVg+RTJ8Y4jjZ02GbLwrpELD2Qs+ohCl8e64G29qutchv6nJq +OdbL5U+d6DKyrzSpZyMRPA+UmB78KsBTs0o3wME7IA9J37YgtpUZifcC4LdgTWrX +2eBICGPqi7GGKzdnI5LDhCJZnHwzva+6lBwa8fW5aXQG69uPTFmd/pNNF6+8f2Fc +YGljQiD6FYVKAUfYRBlw9ymKaIbNmyh9bs71ezPrI4ltOLjmZLZFRouSIaeExfzj +2FkWERNG/iAuEIRolyyXjqjiQIuiEi8uo6sg1cPrD59EuWtTcMzTxuhVU8Ra37F6 +DrMEipqh84zQcnT0i/RNk4K723aB9uWwHJgJ5Y2/6cbta7ZkYsfQfjBC4nBRVyUl +BCpEFYNePbKttYF5Cf5FraMlGzAY0W/MSIUxvRmlkjCzBod4LA+K/hQxEiw7Xa8O +AZfw9l9lmSnia+fgRz3fLKxg3yklw6rA/2aISb83uVRvxgqKym3EeJ/+CsOQpwOb +lEBxWfQizah1Ct4NhsuKLmBbopxAXLqz25E+3BvvsM4nuwWVfoyvTVXYQ+k4V/hj +2iS5buJ5twIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQAGTw5MJurTeeWy+jQikGfi +vrxt9lzqt+uSV8D6V1GBzBAl8m4SqSY0U8KM/gtqh9bhmQwm0qgx/mKcDKzCUKaj +XPKm/NbR+pjZD9Lcx4Iy0iqi9rsxSKECGM2dYAmm7GXnXvz9QUxZjteTgYoRP2s6 +GfosvTQiUEr/cIrYAU3wC0/94pRb9/FLVVon/aVdsh+Dqb4j7BhKLzXNCNjkv1Sv +/YL1zpe/2SPxe0Bfymys97lcu1DB01e/MLfqQJThYOblMte/zGNZO24HcvROIkyo +UtYy5/H4F5rsamSGMNdBfauTtYxz7lOT7qQoDNyGMN9bfjWnkVi/lV2CVooeiHIs +7wLWEhYmU9DiAzcmODU9uMRRBlGOWK8UQg05exc518heICmudSbgSyQLGqzVoI4k +ybhmBA3w93KEXJSXlnU7hBzoYDP2d1g46Ay59UtvLycS1kxe0jVjxxRnh/f9aPbM +wUYBzEC0naUzMeJtElHLHgW4HT6PLgFImgLLFh8dnYJUzn35wz10g3YBA61YUJuO +DpapKHixn/2X/t/8Vf1vqr/VwiwUglNQj+P78Fdb3T56JsYRG1bdf6nz5dvv4qtL +oG+OjPI/tiLjh2ktqaMjeVmlQFchy/C5Lr48d9IGmo+x2ECYSWVvwzxI7PIbYBI4 +oaPjh2zKIrz/AlY2RmqMMA=="; + private const string TestTslUrl = "https://open-eid.github.io/test-TL/tl-mp-test-EE.xml"; + private const string TestTsUrl = "http://demo.sk.ee/tsa/"; + + private readonly IWebHostEnvironment env; + + public DigiDocConfiguration(IWebHostEnvironment env) + { + this.env = env; + } + + public void Initialize() + { + var conf = new DigiDocConf(""); + if (env.IsDevelopment()) + { + conf.setTSLUrl(TestTslUrl); + conf.setTSLCert(Convert.FromBase64String(TestTslCert)); + conf.setTSUrl(TestTsUrl); + } + DigiDocConf.init(conf); + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Signing/SigningService.cs b/example/src/WebEid.AspNetCore.Example/Signing/SigningService.cs new file mode 100644 index 0000000..edb37d5 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Signing/SigningService.cs @@ -0,0 +1,136 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example.Services +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.IO; + using System.Security.Claims; + using System.Security.Cryptography.X509Certificates; + using digidoc; + using Dto; + using Microsoft.Extensions.Logging; + using WebEid.Security.Util; + + public class SigningService : IDisposable + { + private static readonly string FileToSign = Path.Combine("wwwroot", "files", "example-for-signing.txt"); + private readonly DigiDocConfiguration configuration; + private readonly ILogger logger; + private bool _disposedValue; + + public SigningService(DigiDocConfiguration configuration, ILogger logger) + { + this.configuration = configuration; + this.logger = logger; + + // The current implementation of the static DigiDoc library assumes that SigningService is used as a singleton. + // In the ASP.NET Core application, we initialize it using the AddSingleton method in Startup.cs. + // The DigiDoc library is initialized in the constructor and terminated in Dispose. + configuration.Initialize(); + digidoc.initialize("WebEidExample"); + } + + public DigestDto PrepareContainer(CertificateDto data, ClaimsIdentity identity, string tempContainerName) + { + var certificate = new X509Certificate(Convert.FromBase64String(data.CertificateBase64String)); + if (identity.GetIdCode() != certificate.GetSubjectIdCode()) + { + throw new ArgumentException( + "Authenticated subject ID code differs from signing certificate subject ID code"); + } + + this.logger?.LogDebug("Creating container file: '{0}'", tempContainerName); + Container container = Container.create(tempContainerName); + container.addDataFile(FileToSign, "application/octet-stream"); + logger?.LogInformation("Preparing container for signing for file '{0}'", tempContainerName); + var signature = + container.prepareWebSignature(certificate.Export(X509ContentType.Cert), "time-stamp"); + var hashFunction = GetSupportedHashAlgorithm(data.SupportedSignatureAlgorithms, signature.signatureMethod()); + container.save(); + return new DigestDto + { + Hash = Convert.ToBase64String(signature.dataToSign()), + HashFunction = hashFunction + }; + } + + public void SignContainer(SignatureDto signatureDto, string tempContainerName) + { + var container = Container.open(tempContainerName); + var signatureBytes = Convert.FromBase64String(signatureDto.Signature); + var signature = container.signatures().First(); // Container must have one signature as it was added in PrepareContainer + signature.setSignatureValue(signatureBytes); + signature.extendSignatureProfile("time-stamp"); + container.save(); + } + + private static string GetSupportedHashAlgorithm(IList supportedSignatureAlgorithms, string signatureMethod) + { + var framgment = new Uri(signatureMethod).Fragment; + if (supportedSignatureAlgorithms.FirstOrDefault(algo => FragmentEquals(framgment, algo), null) is SignatureAlgorithmDto algo) + { + return algo.HashFunction; + } + throw new ArgumentException("Supported signature algorithm not found"); + } + + private static bool FragmentEquals(string fragment, SignatureAlgorithmDto signatureAlgorithm) => + signatureAlgorithm switch + { + { CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-224" } => fragment == "#ecdsa-sha224", + { CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-256" } => fragment == "#ecdsa-sha256", + { CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-384" } => fragment == "#ecdsa-sha384", + { CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-512" } => fragment == "#ecdsa-sha512", + { CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-224" } => fragment == "#rsa-sha224", + { CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-256" } => fragment == "#rsa-sha256", + { CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-384" } => fragment == "#rsa-sha384", + { CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-512" } => fragment == "#rsa-sha512", + { CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-224" } => fragment == "#sha224-rsa-MGF1", + { CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-256" } => fragment == "#sha256-rsa-MGF1", + { CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-384" } => fragment == "#sha384-rsa-MGF1", + { CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-512" } => fragment == "#sha512-rsa-MGF1", + _ => false + }; + + ~SigningService() => Dispose(false); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // You can release managed resources here if needed. + } + + digidoc.terminate(); + _disposedValue = true; + } + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/Startup.cs b/example/src/WebEid.AspNetCore.Example/Startup.cs new file mode 100644 index 0000000..bbabba4 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/Startup.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2021-2024 Estonian Information System Authority +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace WebEid.AspNetCore.Example +{ + using Certificates; + using Microsoft.AspNetCore.Authentication.Cookies; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.HttpOverrides; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + using Services; + using System; + using System.Security.Cryptography; + using Security.Challenge; + using Security.Validator; + using System.Configuration; + using Microsoft.AspNetCore.Authorization; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + + public class Startup + { + public Startup(IConfiguration configuration, IWebHostEnvironment environment) + { + Configuration = configuration; + CurrentEnvironment = environment; + } + + private IConfiguration Configuration { get; } + private IWebHostEnvironment CurrentEnvironment { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddConsole(); + }); + var logger = loggerFactory.CreateLogger("Web-eId ASP.NET Core Example"); + services.AddSingleton(logger); + + services.AddRazorPages(options => + { + options.Conventions.AuthorizePage("/welcome", "LoggedInOnly"); + }); + + services.AddAuthorization(options => + { + options.AddPolicy("LoggedInOnly", policy => + { + policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme); + policy.RequireAuthenticatedUser(); + policy.Requirements.Add(new LoggedInRequirement()); + }); + }); + + services.AddSingleton(); + + services.AddControllers(); + services.AddMvc(options => + { + options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); + }); + + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => + { + options.Cookie.Name = "__Host-WebEid.AspNetCore.Example.Auth"; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.Cookie.SameSite = SameSiteMode.Strict; + options.Events.OnRedirectToLogin = context => + { + context.Response.Redirect("/"); + return Task.CompletedTask; + }; + options.Events.OnRedirectToAccessDenied = context => + { + context.Response.Redirect("/"); + return Task.CompletedTask; + }; + }); + + services.AddSession(options => + { + options.Cookie.Name = "__Host-WebEid.AspNetCore.Example.Session"; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.Cookie.SameSite = SameSiteMode.Strict; + options.IdleTimeout = TimeSpan.FromSeconds(60); + options.Cookie.IsEssential = true; + }); + + var url = GetOriginUrl(Configuration); + + services.AddSingleton(new AuthTokenValidatorBuilder(logger) + .WithSiteOrigin(url) + .WithTrustedCertificateAuthorities(CertificateLoader.LoadTrustedCaCertificatesFromDisk(CurrentEnvironment.IsDevelopment())) + .Build()); + + services.AddSingleton(RandomNumberGenerator.Create()); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddAntiforgery(options => + { + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + }); + + // Add support for running behind a TLS terminating proxy. + services.Configure(options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + // Only use this if you're behind a known proxy: + options.KnownNetworks.Clear(); + options.KnownProxies.Clear(); + }); + } + + private static Uri GetOriginUrl(IConfiguration configuration) + { + var url = configuration["OriginUrl"]; + if (string.IsNullOrWhiteSpace(url)) + { + throw new ConfigurationErrorsException("OriginUrl is not configured"); + } + + return new Uri(url); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseHttpsRedirection(); + } + else + { + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + // Add support for running behind a TLS terminating proxy. + app.UseForwardedHeaders(); + } + + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseAuthorization(); + app.UseAuthentication(); + app.UseSession(); + + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + endpoints.MapControllers(); + }); + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj b/example/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj new file mode 100644 index 0000000..abbcd36 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj @@ -0,0 +1,55 @@ + + + + net8.0 + aspnet-web_eid_asp_dotnet_example-3FF31439-FDC0-4164-B154-03E005FE96CD + WebEid.AspNetCore.Example + false + Linux + WebEid.AspNetCore.Example + true + 1.2.0 + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + PreserveNewest + + + + diff --git a/example/src/WebEid.AspNetCore.Example/appsettings.Development.json b/example/src/WebEid.AspNetCore.Example/appsettings.Development.json new file mode 100644 index 0000000..a0f43a0 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "OriginUrl": "https://localhost:44391", + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/example/src/WebEid.AspNetCore.Example/appsettings.json b/example/src/WebEid.AspNetCore.Example/appsettings.json new file mode 100644 index 0000000..7d0c71b --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/appsettings.json @@ -0,0 +1,11 @@ +{ + "OriginUrl": "https://localhost:44391", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/css/bootstrap.min.css b/example/src/WebEid.AspNetCore.Example/wwwroot/css/bootstrap.min.css new file mode 100644 index 0000000..7d2a868 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.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-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{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);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/css/main.css b/example/src/WebEid.AspNetCore.Example/wwwroot/css/main.css new file mode 100644 index 0000000..3de3421 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/css/main.css @@ -0,0 +1,69 @@ +body { + font-family: "Inv Maison Neue","Maison Neue",-apple-system,BlinkMacSystemFont,"Open Sans",open-sans,sans-serif; +} + +.has-advanced-upload { + outline: 2px dashed #92b0b3; + outline-offset: -10px; + + -webkit-transition: outline-offset .25s ease-in-out, background-color .25s linear; + transition: outline-offset .25s ease-in-out, background-color .25s linear; +} + +.adding-signature { + height: 14px; + color: #000000; + font-size: 32px; + font-weight: bold; + letter-spacing: 0; + line-height: 14px; +} + +.welcome-line { + height: 40px; + color: #000000; + font-size: 14px; + letter-spacing: 0; + line-height: 20px; + margin-top: 1rem; +} + +#webeid-logout-button { + height: 2.5rem; +} + +#file-drop-area { + margin: 0 10rem; + padding-top: 2rem; + box-sizing: border-box; + outline: 2px dashed #92b0b3; + outline-offset: -10px; + border-radius: 3px; + background-color: #F5F5F5; +} + +.is-dragover { + background-color: #b7dbde !important; + outline-offset: -20px !important; + outline-color: #ffffff !important; +} + +#file-name, #file-drop-area, .welcome-line { + text-align: center; +} + +.eu-logo-fixed { + display: block; + position: fixed; + background: #fff; + bottom: 0; + left: 0; + margin: 0; + padding: 5px 20px; + text-align: center; + z-index: 1000; +} + +.eu-logo-fixed img { + height: 86px; +} diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/favicon.ico b/example/src/WebEid.AspNetCore.Example/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..63e859b476eff5055e0e557aaa151ca8223fbeef GIT binary patch literal 5430 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 XZz9n9el{XBaPz`JLPBTs)j|IQPi<*H literal 0 HcmV?d00001 diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/files/example-for-signing.txt b/example/src/WebEid.AspNetCore.Example/wwwroot/files/example-for-signing.txt new file mode 100644 index 0000000..07a5be0 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/files/example-for-signing.txt @@ -0,0 +1 @@ +This is an example text file for testing digital signing. \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/img/eu-fund-flags.svg b/example/src/WebEid.AspNetCore.Example/wwwroot/img/eu-fund-flags.svg new file mode 100644 index 0000000..0e99b0d --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/img/eu-fund-flags.svg @@ -0,0 +1,787 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/js/errors.js b/example/src/WebEid.AspNetCore.Example/wwwroot/js/errors.js new file mode 100644 index 0000000..95220bb --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/js/errors.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020-2024 Estonian Information System Authority + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +"use strict"; + +const alertUi = { + alert: document.querySelector("#error-message"), + alertMessage: document.querySelector("#error-message .message"), + alertDetails: document.querySelector("#error-message .details") +}; + +export function hideErrorMessage() { + alertUi.alert.style.display = "none"; +} + +export function showErrorMessage(error) { + const message = "Authentication failed"; + const details = + `[Code]\n${error.code}` + + `\n\n[Message]\n${error.message}` + + (error.response ? `\n\n[response]\n${JSON.stringify(error.response, null, " ")}` : ""); + + alertUi.alertMessage.innerText = message; + alertUi.alertDetails.innerText = details; + alertUi.alert.style.display = "block"; +} + +export async function checkHttpError(response) { + if (!response.ok) { + let body; + try { + body = await response.text(); + } catch (error) { + body = "<>"; + } + const error = new Error("Server error: " + body); + error.code = response.status; + throw error; + } +} \ No newline at end of file diff --git a/example/src/WebEid.AspNetCore.Example/wwwroot/js/web-eid.js b/example/src/WebEid.AspNetCore.Example/wwwroot/js/web-eid.js new file mode 100644 index 0000000..ee9b770 --- /dev/null +++ b/example/src/WebEid.AspNetCore.Example/wwwroot/js/web-eid.js @@ -0,0 +1,423 @@ +/** + * MIT License + * + * Copyright (c) 2020-2022 Estonian Information System Authority + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +var Action; +(function (Action) { + Action["WARNING"] = "web-eid:warning"; + Action["STATUS"] = "web-eid:status"; + Action["STATUS_ACK"] = "web-eid:status-ack"; + Action["STATUS_SUCCESS"] = "web-eid:status-success"; + Action["STATUS_FAILURE"] = "web-eid:status-failure"; + Action["AUTHENTICATE"] = "web-eid:authenticate"; + Action["AUTHENTICATE_ACK"] = "web-eid:authenticate-ack"; + Action["AUTHENTICATE_SUCCESS"] = "web-eid:authenticate-success"; + Action["AUTHENTICATE_FAILURE"] = "web-eid:authenticate-failure"; + Action["GET_SIGNING_CERTIFICATE"] = "web-eid:get-signing-certificate"; + Action["GET_SIGNING_CERTIFICATE_ACK"] = "web-eid:get-signing-certificate-ack"; + Action["GET_SIGNING_CERTIFICATE_SUCCESS"] = "web-eid:get-signing-certificate-success"; + Action["GET_SIGNING_CERTIFICATE_FAILURE"] = "web-eid:get-signing-certificate-failure"; + Action["SIGN"] = "web-eid:sign"; + Action["SIGN_ACK"] = "web-eid:sign-ack"; + Action["SIGN_SUCCESS"] = "web-eid:sign-success"; + Action["SIGN_FAILURE"] = "web-eid:sign-failure"; +})(Action || (Action = {})); +var Action$1 = Action; + +var ErrorCode; +(function (ErrorCode) { + ErrorCode["ERR_WEBEID_ACTION_TIMEOUT"] = "ERR_WEBEID_ACTION_TIMEOUT"; + ErrorCode["ERR_WEBEID_USER_TIMEOUT"] = "ERR_WEBEID_USER_TIMEOUT"; + ErrorCode["ERR_WEBEID_VERSION_MISMATCH"] = "ERR_WEBEID_VERSION_MISMATCH"; + ErrorCode["ERR_WEBEID_VERSION_INVALID"] = "ERR_WEBEID_VERSION_INVALID"; + ErrorCode["ERR_WEBEID_EXTENSION_UNAVAILABLE"] = "ERR_WEBEID_EXTENSION_UNAVAILABLE"; + ErrorCode["ERR_WEBEID_NATIVE_UNAVAILABLE"] = "ERR_WEBEID_NATIVE_UNAVAILABLE"; + ErrorCode["ERR_WEBEID_UNKNOWN_ERROR"] = "ERR_WEBEID_UNKNOWN_ERROR"; + ErrorCode["ERR_WEBEID_CONTEXT_INSECURE"] = "ERR_WEBEID_CONTEXT_INSECURE"; + ErrorCode["ERR_WEBEID_USER_CANCELLED"] = "ERR_WEBEID_USER_CANCELLED"; + ErrorCode["ERR_WEBEID_NATIVE_INVALID_ARGUMENT"] = "ERR_WEBEID_NATIVE_INVALID_ARGUMENT"; + ErrorCode["ERR_WEBEID_NATIVE_FATAL"] = "ERR_WEBEID_NATIVE_FATAL"; + ErrorCode["ERR_WEBEID_ACTION_PENDING"] = "ERR_WEBEID_ACTION_PENDING"; + ErrorCode["ERR_WEBEID_MISSING_PARAMETER"] = "ERR_WEBEID_MISSING_PARAMETER"; +})(ErrorCode || (ErrorCode = {})); +var ErrorCode$1 = ErrorCode; + +class MissingParameterError extends Error { + constructor(message) { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_MISSING_PARAMETER; + } +} + +class ActionPendingError extends Error { + constructor(message = "same action for Web-eID browser extension is already pending") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_ACTION_PENDING; + } +} + +class ActionTimeoutError extends Error { + constructor(message = "extension message timeout") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_ACTION_TIMEOUT; + } +} + +const SECURE_CONTEXTS_INFO_URL = "https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"; +class ContextInsecureError extends Error { + constructor(message = "Secure context required, see " + SECURE_CONTEXTS_INFO_URL) { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_CONTEXT_INSECURE; + } +} + +class ExtensionUnavailableError extends Error { + constructor(message = "Web-eID extension is not available") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_EXTENSION_UNAVAILABLE; + } +} + +var config = Object.freeze({ + VERSION: "2.0.1", + EXTENSION_HANDSHAKE_TIMEOUT: 1000, + NATIVE_APP_HANDSHAKE_TIMEOUT: 5 * 1000, + DEFAULT_USER_INTERACTION_TIMEOUT: 2 * 60 * 1000, + MAX_EXTENSION_LOAD_DELAY: 1000, +}); + +class NativeFatalError extends Error { + constructor(message = "native application terminated with a fatal error") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_NATIVE_FATAL; + } +} + +class NativeInvalidArgumentError extends Error { + constructor(message = "native application received an invalid argument") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_NATIVE_INVALID_ARGUMENT; + } +} + +class NativeUnavailableError extends Error { + constructor(message = "Web-eID native application is not available") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_NATIVE_UNAVAILABLE; + } +} + +class UnknownError extends Error { + constructor(message = "an unknown error occurred") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_UNKNOWN_ERROR; + } +} + +class UserCancelledError extends Error { + constructor(message = "request was cancelled by the user") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_USER_CANCELLED; + } +} + +class UserTimeoutError extends Error { + constructor(message = "user failed to respond in time") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_USER_TIMEOUT; + } +} + +class VersionInvalidError extends Error { + constructor(message = "invalid version string") { + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_VERSION_INVALID; + } +} + +function tmpl(strings, requiresUpdate) { + return `Update required for Web-eID ${requiresUpdate}`; +} +class VersionMismatchError extends Error { + constructor(message, versions, requiresUpdate) { + if (!message) { + if (!requiresUpdate) { + message = "requiresUpdate not provided"; + } + else if (requiresUpdate.extension && requiresUpdate.nativeApp) { + message = tmpl `${"extension and native app"}`; + } + else if (requiresUpdate.extension) { + message = tmpl `${"extension"}`; + } + else if (requiresUpdate.nativeApp) { + message = tmpl `${"native app"}`; + } + } + super(message); + this.name = this.constructor.name; + this.code = ErrorCode$1.ERR_WEBEID_VERSION_MISMATCH; + this.requiresUpdate = requiresUpdate; + if (versions) { + const { library, extension, nativeApp } = versions; + Object.assign(this, { library, extension, nativeApp }); + } + } +} + +const errorCodeToErrorClass = { + [ErrorCode$1.ERR_WEBEID_ACTION_PENDING]: ActionPendingError, + [ErrorCode$1.ERR_WEBEID_ACTION_TIMEOUT]: ActionTimeoutError, + [ErrorCode$1.ERR_WEBEID_CONTEXT_INSECURE]: ContextInsecureError, + [ErrorCode$1.ERR_WEBEID_EXTENSION_UNAVAILABLE]: ExtensionUnavailableError, + [ErrorCode$1.ERR_WEBEID_NATIVE_INVALID_ARGUMENT]: NativeInvalidArgumentError, + [ErrorCode$1.ERR_WEBEID_NATIVE_FATAL]: NativeFatalError, + [ErrorCode$1.ERR_WEBEID_NATIVE_UNAVAILABLE]: NativeUnavailableError, + [ErrorCode$1.ERR_WEBEID_USER_CANCELLED]: UserCancelledError, + [ErrorCode$1.ERR_WEBEID_USER_TIMEOUT]: UserTimeoutError, + [ErrorCode$1.ERR_WEBEID_VERSION_INVALID]: VersionInvalidError, + [ErrorCode$1.ERR_WEBEID_VERSION_MISMATCH]: VersionMismatchError, +}; +function deserializeError(errorObject) { + let error; + if (typeof errorObject.code == "string" && errorObject.code in errorCodeToErrorClass) { + const CustomError = errorCodeToErrorClass[errorObject.code]; + error = new CustomError(); + } + else { + error = new UnknownError(); + } + for (const [key, value] of Object.entries(errorObject)) { + error[key] = value; + } + return error; +} + +class WebExtensionService { + constructor() { + this.loggedWarnings = []; + this.queue = []; + window.addEventListener("message", (event) => this.receive(event)); + } + receive(event) { + var _a, _b, _c, _d, _e, _f; + if (!/^web-eid:/.test((_a = event.data) === null || _a === void 0 ? void 0 : _a.action)) + return; + const message = event.data; + const suffix = (_c = (_b = message.action) === null || _b === void 0 ? void 0 : _b.match(/success$|failure$|ack$/)) === null || _c === void 0 ? void 0 : _c[0]; + const initialAction = this.getInitialAction(message.action); + const pending = this.getPendingMessage(initialAction); + if (message.action === Action$1.WARNING) { + (_d = message.warnings) === null || _d === void 0 ? void 0 : _d.forEach((warning) => { + if (!this.loggedWarnings.includes(warning)) { + this.loggedWarnings.push(warning); + console.warn(warning); + } + }); + } + else if (pending) { + switch (suffix) { + case "ack": { + clearTimeout(pending.ackTimer); + break; + } + case "success": { + this.removeFromQueue(initialAction); + (_e = pending.resolve) === null || _e === void 0 ? void 0 : _e.call(pending, message); + break; + } + case "failure": { + const failureMessage = message; + this.removeFromQueue(initialAction); + (_f = pending.reject) === null || _f === void 0 ? void 0 : _f.call(pending, failureMessage.error ? deserializeError(failureMessage.error) : failureMessage); + break; + } + } + } + } + send(message, timeout) { + if (this.getPendingMessage(message.action)) { + return Promise.reject(new ActionPendingError()); + } + else if (!window.isSecureContext) { + return Promise.reject(new ContextInsecureError()); + } + else { + const pending = { message }; + this.queue.push(pending); + pending.promise = new Promise((resolve, reject) => { + pending.resolve = resolve; + pending.reject = reject; + }); + pending.ackTimer = window.setTimeout(() => this.onAckTimeout(pending), config.EXTENSION_HANDSHAKE_TIMEOUT); + pending.replyTimer = window.setTimeout(() => this.onReplyTimeout(pending), timeout); + window.postMessage(message, "*"); + return pending.promise; + } + } + onReplyTimeout(pending) { + var _a; + this.removeFromQueue(pending.message.action); + (_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new ActionTimeoutError()); + } + onAckTimeout(pending) { + var _a; + clearTimeout(pending.replyTimer); + this.removeFromQueue(pending.message.action); + (_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new ExtensionUnavailableError()); + } + getPendingMessage(action) { + return this.queue.find((pm) => { + return pm.message.action === action; + }); + } + getInitialAction(action) { + return action.replace(/-success$|-failure$|-ack$/, ""); + } + removeFromQueue(action) { + const pending = this.getPendingMessage(action); + clearTimeout(pending === null || pending === void 0 ? void 0 : pending.replyTimer); + this.queue = this.queue.filter((pending) => (pending.message.action !== action)); + } +} + +/** + * Sleeps for a specified time before resolving the returned promise. + * + * @param milliseconds Time in milliseconds until the promise is resolved + * + * @returns Empty promise + */ +function sleep(milliseconds) { + return new Promise((resolve) => { + setTimeout(() => resolve(), milliseconds); + }); +} + +const webExtensionService = new WebExtensionService(); +const initializationTime = +new Date(); +async function extensionLoadDelay() { + const now = +new Date(); + await sleep(initializationTime + config.MAX_EXTENSION_LOAD_DELAY - now); +} +async function status() { + await extensionLoadDelay(); + const timeout = config.EXTENSION_HANDSHAKE_TIMEOUT + config.NATIVE_APP_HANDSHAKE_TIMEOUT; + const message = { + action: Action$1.STATUS, + libraryVersion: config.VERSION, + }; + try { + const { library, extension, nativeApp, } = await webExtensionService.send(message, timeout); + return { + library, + extension, + nativeApp, + }; + } + catch (error) { + error.library = config.VERSION; + throw error; + } +} +async function authenticate(challengeNonce, options) { + await extensionLoadDelay(); + if (!challengeNonce) { + throw new MissingParameterError("authenticate function requires a challengeNonce"); + } + const timeout = (config.EXTENSION_HANDSHAKE_TIMEOUT + + config.NATIVE_APP_HANDSHAKE_TIMEOUT + + ((options === null || options === void 0 ? void 0 : options.userInteractionTimeout) || config.DEFAULT_USER_INTERACTION_TIMEOUT)); + const message = { + action: Action$1.AUTHENTICATE, + libraryVersion: config.VERSION, + challengeNonce, + options, + }; + const { unverifiedCertificate, algorithm, signature, format, appVersion, } = await webExtensionService.send(message, timeout); + return { + unverifiedCertificate, + algorithm, + signature, + format, + appVersion, + }; +} +async function getSigningCertificate(options) { + await extensionLoadDelay(); + const timeout = (config.EXTENSION_HANDSHAKE_TIMEOUT + + config.NATIVE_APP_HANDSHAKE_TIMEOUT + + ((options === null || options === void 0 ? void 0 : options.userInteractionTimeout) || config.DEFAULT_USER_INTERACTION_TIMEOUT) * 2); + const message = { + action: Action$1.GET_SIGNING_CERTIFICATE, + libraryVersion: config.VERSION, + options, + }; + const { certificate, supportedSignatureAlgorithms, } = await webExtensionService.send(message, timeout); + return { + certificate, + supportedSignatureAlgorithms, + }; +} +async function sign(certificate, hash, hashFunction, options) { + await extensionLoadDelay(); + if (!certificate) { + throw new MissingParameterError("sign function requires a certificate as parameter"); + } + if (!hash) { + throw new MissingParameterError("sign function requires a hash as parameter"); + } + if (!hashFunction) { + throw new MissingParameterError("sign function requires a hashFunction as parameter"); + } + const timeout = (config.EXTENSION_HANDSHAKE_TIMEOUT + + config.NATIVE_APP_HANDSHAKE_TIMEOUT + + ((options === null || options === void 0 ? void 0 : options.userInteractionTimeout) || config.DEFAULT_USER_INTERACTION_TIMEOUT) * 2); + const message = { + action: Action$1.SIGN, + libraryVersion: config.VERSION, + certificate, + hash, + hashFunction, + options, + }; + const { signature, signatureAlgorithm, } = await webExtensionService.send(message, timeout); + return { + signature, + signatureAlgorithm, + }; +} + +export { Action$1 as Action, ErrorCode$1 as ErrorCode, authenticate, config, getSigningCertificate, sign, status }; diff --git a/example/src/docker-compose.yml b/example/src/docker-compose.yml new file mode 100644 index 0000000..41cff12 --- /dev/null +++ b/example/src/docker-compose.yml @@ -0,0 +1,8 @@ +version: '2' +services: + web-eid-asp-dotnet-example: + image: registry.gitlab.com/web-eid/service/web-eid-authentication-token-validation-dotnet/web-eid-asp-dotnet-example + restart: always + ports: + - '127.0.0.1:8480:80' + diff --git a/example/src/ria_public_key.gpg b/example/src/ria_public_key.gpg new file mode 100644 index 0000000000000000000000000000000000000000..1630297842655e2444ee8e2ff4fbc4e848f67e01 GIT binary patch literal 2215 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 literal 0 HcmV?d00001